Grundlagen des iterativen Prozesses

PythonPythonBeginner
Jetzt üben

This tutorial is from open-source community. Access the source code

💡 Dieser Artikel wurde von AI-Assistenten übersetzt. Um die englische Version anzuzeigen, können Sie hier klicken

Einführung

In diesem Abschnitt wird der zugrunde liegende Prozess der Iteration betrachtet.

Iteration überall

Viele verschiedene Objekte unterstützen die Iteration.

a = 'hello'
for c in a: ## Schleife über die Zeichen in a
  ...

b = { 'name': 'Dave', 'password': 'foo'}
for k in b: ## Schleife über die Schlüssel im Dictionary
  ...

c = [1,2,3,4]
for i in c: ## Schleife über die Elemente in einer Liste/Tupel
  ...

f = open('foo.txt')
for x in f: ## Schleife über die Zeilen in einer Datei
  ...

Iteration: Protokoll

Betrachten Sie die for-Anweisung.

for x in obj:
    ## Anweisungen

Was geschieht hinter den Kulissen?

_iter = obj.__iter__()        ## Hole das Iterationsobjekt
while True:
    try:
        x = _iter.__next__()  ## Hole das nächste Element
        ## Anweisungen...
    except StopIteration:     ## Es gibt keine weiteren Elemente
        break

Alle Objekte, die mit der for-Schleife zusammenarbeiten, implementieren dieses niedrigere Iterationsprotokoll.

Beispiel: Manuelle Iteration über eine Liste.

>>> x = [1,2,3]
>>> it = x.__iter__()
>>> it
<listiterator object at 0x590b0>
>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
3
>>> it.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in? StopIteration
>>>

Iteration unterstützen

Wenn Sie die Iteration zu Ihren eigenen Objekten hinzufügen möchten, ist es hilfreich, sich mit ihr vertraut zu machen. Beispielsweise bei der Erstellung eines benutzerdefinierten Containers.

class Portfolio:
    def __init__(self):
        self.holdings = []

    def __iter__(self):
        return self.holdings.__iter__()
  ...

port = Portfolio()
for s in port:
  ...

Übung 6.1: Iteration veranschaulicht

Erstellen Sie die folgende Liste:

a = [1,9,4,25,16]

Iterieren Sie manuell über diese Liste. Rufen Sie __iter__() auf, um einen Iterator zu erhalten, und rufen Sie die __next__()-Methode auf, um aufeinanderfolgende Elemente zu erhalten.

>>> i = a.__iter__()
>>> i
<listiterator object at 0x64c10>
>>> i.__next__()
1
>>> i.__next__()
9
>>> i.__next__()
4
>>> i.__next__()
25
>>> i.__next__()
16
>>> i.__next__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

Die integrierte Funktion next() ist eine Abkürzung für das Aufrufen der __next__()-Methode eines Iterators. Versuchen Sie, sie auf einer Datei zu verwenden:

>>> f = open('portfolio.csv')
>>> f.__iter__()    ## Hinweis: Dies gibt die Datei selbst zurück
<_io.TextIOWrapper name='portfolio.csv' mode='r' encoding='UTF-8'>
>>> next(f)
'name,shares,price\n'
>>> next(f)
'"AA",100,32.20\n'
>>> next(f)
'"IBM",50,91.10\n'
>>>

Rufen Sie immer wieder next(f) auf, bis Sie am Ende der Datei angelangt sind. Beobachten Sie, was passiert.

Übung 6.2: Iteration unterstützen

Manchmal möchten Sie möglicherweise, dass eines Ihrer eigenen Objekte die Iteration unterstützt - insbesondere wenn Ihr Objekt um eine vorhandene Liste oder andere Iterable wrappt. In einer neuen Datei portfolio.py definieren Sie die folgende Klasse:

## portfolio.py

class Portfolio:

    def __init__(self, holdings):
        self._holdings = holdings

    @property
    def total_cost(self):
        return sum([s.cost for s in self._holdings])

    def tabulate_shares(self):
        from collections import Counter
        total_shares = Counter()
        for s in self._holdings:
            total_shares[s.name] += s.shares
        return total_shares

Diese Klasse soll als Schicht um eine Liste dienen, aber mit zusätzlichen Methoden wie der total_cost-Eigenschaft. Ändern Sie die read_portfolio()-Funktion in report.py so, dass sie eine Portfolio-Instanz wie folgt erstellt:

## report.py

...

import fileparse
from stock import Stock
from portfolio import Portfolio

def read_portfolio(filename):
    '''
    Liest eine Datei mit einem Aktienportfolio in eine Liste von Wörterbüchern mit den Schlüsseln
    name, shares und price ein.
    '''
    with open(filename) as file:
        portdicts = fileparse.parse_csv(file,
                                        select=['name','shares','price'],
                                        types=[str,int,float])

    portfolio = [ Stock(d['name'], d['shares'], d['price']) for d in portdicts ]
    return Portfolio(portfolio)

...

Versuchen Sie, das report.py-Programm auszuführen. Sie werden feststellen, dass es aufgrund des Fehlers, dass Portfolio-Instanzen nicht iterierbar sind, spektakulär fehlschlägt.

>>> import report
>>> report.portfolio_report('portfolio.csv', 'prices.csv')
... abstürzt...

Beheben Sie dies, indem Sie die Portfolio-Klasse so ändern, dass sie die Iteration unterstützt:

class Portfolio:

    def __init__(self, holdings):
        self._holdings = holdings

    def __iter__(self):
        return self._holdings.__iter__()

    @property
    def total_cost(self):
        return sum([s.shares*s.price for s in self._holdings])

    def tabulate_shares(self):
        from collections import Counter
        total_shares = Counter()
        for s in self._holdings:
            total_shares[s.name] += s.shares
        return total_shares

Nachdem Sie diese Änderung vorgenommen haben, sollte Ihr report.py-Programm wieder funktionieren. Während Sie dabei sind, verbessern Sie auch Ihr pcost.py-Programm, um das neue Portfolio-Objekt zu verwenden. So:

## pcost.py

import report

def portfolio_cost(filename):
    '''
    Berechnet die Gesamtkosten (Anteile * Preis) einer Portfolio-Datei
    '''
    portfolio = report.read_portfolio(filename)
    return portfolio.total_cost
...

Testen Sie es, um sicherzustellen, dass es funktioniert:

>>> import pcost
>>> pcost.portfolio_cost('portfolio.csv')
44671.15
>>>

Übung 6.3: Ein passenderer Container erstellen

Wenn Sie eine Containerklasse erstellen, möchten Sie oft mehr tun als nur Iteration. Ändern Sie die Portfolio-Klasse so, dass sie einige andere spezielle Methoden hat, wie folgt:

class Portfolio:
    def __init__(self, holdings):
        self._holdings = holdings

    def __iter__(self):
        return self._holdings.__iter__()

    def __len__(self):
        return len(self._holdings)

    def __getitem__(self, index):
        return self._holdings[index]

    def __contains__(self, name):
        return any([s.name == name for s in self._holdings])

    @property
    def total_cost(self):
        return sum([s.shares*s.price for s in self._holdings])

    def tabulate_shares(self):
        from collections import Counter
        total_shares = Counter()
        for s in self._holdings:
            total_shares[s.name] += s.shares
        return total_shares

Jetzt machen Sie einige Experimente mit dieser neuen Klasse:

>>> import report
>>> portfolio = report.read_portfolio('portfolio.csv')
>>> len(portfolio)
7
>>> portfolio[0]
Stock('AA', 100, 32.2)
>>> portfolio[1]
Stock('IBM', 50, 91.1)
>>> portfolio[0:3]
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44)]
>>> 'IBM' in portfolio
True
>>> 'AAPL' in portfolio
False
>>>

Eine wichtige Beobachtung dazu - allgemein wird Code als "pythonisch" angesehen, wenn er das übliche Vokabular der Art, wie andere Teile von Python normalerweise funktionieren, spricht. Für Containerobjekte ist das Unterstützen von Iteration, Indexierung, Enthaltensein und anderen Arten von Operatoren ein wichtiger Teil hiervon.

Zusammenfassung

Herzlichen Glückwunsch! Sie haben das Lab zu dem Iterationsprotokoll abgeschlossen. Sie können in LabEx weitere Labs absolvieren, um Ihre Fähigkeiten zu verbessern.

OSZAR »