2016-08-01 23 views
0

Ich habe die folgende Wrapper für einen Wörterbuch bekommt:Wie kann "next" implementiert werden, damit ein Wörterbuchobjekt iterierbar ist?

class MyDict: 
    def __init__(self): 
     self.container = {} 

    def __setitem__(self, key, value): 
     self.container[key] = value 

    def __getitem__(self, key): 
     return self.container[key] 

    def __iter__(self): 
     return self 

    def next(self): 
     pass 

dic = MyDict() 
dic['a'] = 1 
dic['b'] = 2 

for key in dic: 
    print key 

Mein Problem ist, dass ich weiß nicht, wie die next Methode implementieren MyDict iterable zu machen. Jeder Rat würde geschätzt werden.

Antwort

7

Wörterbücher sind kein Selbst Iterator (die sich nur über einmal laufen werden können). Sie machen sie normalerweise iterable, ein Objekt, für das Sie mehrere Iteratoren stattdessen produzieren können.

Lassen Sie die Methode next insgesamt fallen, und geben Sie bei jedem Aufruf ein iterbares Objekt mit __iter__ zurück. Das kann so einfach sein wie nur einen Iterator für self.container Rückkehr:

def __iter__(self): 
    return iter(self.container) 

Wenn Sie müssen Ihre Klasse machen ein Iterator, musst du irgendwie eine aktuelle Iteration Position verfolgen und erhöhen StopIteration, sobald Sie erreichen die ' Ende'. Eine naive Implementierung könnte das iter(self.container) Objekt auf self das erste Mal __iter__ genannt wird gespeichert sein:

def __iter__(self): 
    return self 

def next(self): 
    if not hasattr(self, '_iter'): 
     self._iter = iter(self.container) 
    return next(self._iter) 

an welchem ​​Punkt des iter(self.container) Objekt übernimmt Iteration Position für Sie von Tracking und wird StopIteration erhöhen, wenn das Ende erreicht ist . Es wird auch eine Ausnahme ausgelöst, wenn das zugrundeliegende Wörterbuch geändert wurde (wenn Schlüssel hinzugefügt oder gelöscht wurden) und die Iterationsreihenfolge unterbrochen wurde.

Ein anderer Weg, dies zu tun wäre, zu speichern, nur in ganzzahligen Position und Index in list(self.container) jedes Mal, und einfach die Tatsache ignorieren, dass Einfügen oder Löschen die Iterationsreihenfolge eines Wörterbuchs ändern kann:

_iter_index = 0 

def __iter__(self): 
    return self 

def next(self): 
    idx = self._iter_index 
    if idx is None or idx >= len(self.container): 
     # once we reach the end, all iteration is done, end of. 
     self._iter_index = None 
     raise StopIteration() 
    value = list(self.container)[idx] 
    self._iter_index = idx + 1 
    return value 

In In beiden Fällen ist Ihr Objekt dann ein Iterator, der nur über einmal iteriert werden kann. Wenn Sie das Ende erreicht haben, können Sie es nicht erneut starten.

2

Wenn Sie Ihr dict-ähnliches Objekt beispielsweise in verschachtelten Schleifen oder in einer anderen Anwendung verwenden möchten, die mehrere Iterationen über dasselbe Objekt erfordert, müssen Sie eine __iter__-Methode implementieren, die eine neuere Methode zurückgibt. erzeugtes Iterator-Objekt.

Pythons iterable Objekte alle dies tun:

>>> [1, 2, 3].__iter__() 
<listiterator object at 0x7f67146e53d0> 
>>> iter([1, 2, 3]) # A simpler equivalent 
<listiterator object at 0x7f67146e5390> 

Die einfachste Sache für __iter__ Methode Ihre Objekte zu tun wäre, einen Iterator über die zugrunde liegenden dict zurückzukehren, wie folgt aus:

def __iter__(self): 
    return iter(self.container) 

Für mehr Details, als Sie wahrscheinlich jemals benötigen werden, siehe this Github repository.