Ich benutze Python 2.7.3.Verwendet das Entpacken von Argumenten Iteration oder Item-Getting?
Betrachten Sie eine Dummy-Klasse mit benutzerdefinierten (wenn auch schlecht) Iteration und item-bekommen Verhalten:
class FooList(list):
def __iter__(self):
return iter(self)
def next(self):
return 3
def __getitem__(self, idx):
return 3
ein Exempel statuieren und das seltsame Verhalten sehen:
>>> zz = FooList([1,2,3])
>>> [x for x in zz]
# Hangs because of the self-reference in `__iter__`.
>>> zz[0]
3
>>> zz[1]
3
Aber jetzt wollen wir eine machen Funktion und führen Sie dann auspacken Argument auf zz
:
def add3(a, b, c):
return a + b + c
>>> add3(*zz)
6
# I expected either 9 or for the interpreter to hang like the comprehension!
So Argument un packing erhält die Item-Daten irgendwie von zz
, aber nicht, indem es entweder über das Objekt mit seinem implementierten Iterator iteriert und auch nicht, indem er einen Iterator des armen Mannes ausführt und __getitem__
für so viele Gegenstände aufruft, wie das Objekt hat.
Also die Frage ist: Wie erhält die Syntax add3(*zz)
die Daten Mitglieder von zz
wenn nicht durch diese Methoden? Fehle ich gerade ein anderes allgemeines Muster, um Datenmitglieder von einem solchen Typ zu bekommen?
Mein Ziel ist es, zu sehen, ob ich eine Klasse schreiben könnte, die Iteration oder Item-Getting so implementiert, dass sie ändert, was die Argument-Entpackungssyntax für diese Klasse bedeutet. Nachdem ich das obige Beispiel ausprobiert habe, frage ich mich jetzt, wie das Entpacken von Argumenten bei den zugrunde liegenden Daten ankommt und ob der Programmierer dieses Verhalten beeinflussen kann. Google gab dafür nur eine Menge Ergebnisse zurück, die die grundlegende Verwendung der Syntax *args
erklären.
Ich habe keinen Anwendungsfall dafür, und ich behaupte nicht, dass es eine gute Idee ist. Ich will nur sehen, wie es aus Neugierde gemacht wird.
Added
Da die eingebauten Typen sind speziell behandelt, hier ist ein Beispiel mit object
wo ich mein eigenes bekommen und setzen Verhalten nur eine Liste Objekt pflegen und implementieren Liste zu emulieren.
class FooList(object):
def __init__(self, lst):
self.lst = lst
def __iter__(self): raise ValueError
def next(self): return 3
def __getitem__(self, idx): return self.lst.__getitem__(idx)
def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)
In diesem Fall
In [234]: zz = FooList([1,2,3])
In [235]: [x for x in zz]
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-235-ad3bb7659c84> in <module>()
----> 1 [x for x in zz]
<ipython-input-233-dc9284300db1> in __iter__(self)
2 def __init__(self, lst):
3 self.lst = lst
----> 4 def __iter__(self): raise ValueError
5 def next(self): return 3
6 def __getitem__(self, idx): return self.lst.__getitem__(idx)
ValueError:
In [236]: add_3(*zz)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-236-f9bbfdc2de5c> in <module>()
----> 1 add_3(*zz)
<ipython-input-233-dc9284300db1> in __iter__(self)
2 def __init__(self, lst):
3 self.lst = lst
----> 4 def __iter__(self): raise ValueError
5 def next(self): return 3
6 def __getitem__(self, idx): return self.lst.__getitem__(idx)
ValueError:
Aber statt, wenn ich Iteration stoppt sicherzustellen, und gibt immer 3, kann ich bekommen, was ich mit im ersten Fall zu spielen, um geschossen wurde:
class FooList(object):
def __init__(self, lst):
self.lst = lst
self.iter_loc = -1
def __iter__(self): return self
def next(self):
if self.iter_loc < len(self.lst)-1:
self.iter_loc += 1
return 3
else:
self.iter_loc = -1
raise StopIteration
def __getitem__(self, idx): return self.lst.__getitem__(idx)
def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)
Dann sehe ich das, was ist das, was ich ursprünglich erwartet:
In [247]: zz = FooList([1,2,3])
In [248]: ix = iter(zz)
In [249]: ix.next()
Out[249]: 3
In [250]: ix.next()
Out[250]: 3
In [251]: ix.next()
Out[251]: 3
In [252]: ix.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-252-29d4ae900c28> in <module>()
----> 1 ix.next()
<ipython-input-246-5479fdc9217b> in next(self)
10 else:
11 self.iter_loc = -1
---> 12 raise StopIteration
13 def __getitem__(self, idx): return self.lst.__getitem__(idx)
14 def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)
StopIteration:
In [253]: ix = iter(zz)
In [254]: ix.next()
Out[254]: 3
In [255]: ix.next()
Out[255]: 3
In [256]: ix.next()
Out[256]: 3
In [257]: ix.next()
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-257-29d4ae900c28> in <module>()
----> 1 ix.next()
<ipython-input-246-5479fdc9217b> in next(self)
10 else:
11 self.iter_loc = -1
---> 12 raise StopIteration
13 def __getitem__(self, idx): return self.lst.__getitem__(idx)
14 def __setitem__(self, idx, itm): self.lst.__setitem__(idx, itm)
StopIteration:
In [258]: add_3(*zz)
Out[258]: 9
In [259]: zz[0]
Out[259]: 1
In [260]: zz[1]
Out[260]: 2
In [261]: zz[2]
Out[261]: 3
In [262]: [x for x in zz]
Out[262]: [3, 3, 3]
Zusammenfassung
Die Syntax
*args
nur auf Iteration beruht. Für integrierte Typen geschieht dies auf eine Weise, die in Klassen, die vom integrierten Typ erben, nicht direkt außer Kraft gesetzt werden kann.Diese beiden sind funktional äquivalent:
foo(*[x for x in args])
foo(*args)
Diese sind auch für die Finite-Daten-Strukturen nicht gleichwertig.
foo(*args)
foo(*[args[i] for i in range(len(args))])
Ich glaube, Sie werden interessantere Informationen finden, wenn Sie 'FooList' von' Objekt' anstatt von 'Liste' ableiten. –
Dies hängt nicht wegen unendlicher Iteration; Es hängt, weil '__iter __ (self)' sich indirekt rekursiv aufruft, also nimmt es eine unendliche Rekursion zu sogar _get_ einem Iterator. Sie können das 'next' vollständig entfernen und das gleiche Verhalten bekommen ... – abarnert
Ich habe auch eine separate' FooListIter' Klasse erstellt und hatte '__iter__' eine Instanz davon zurückgeben, mit dem gleichen' next' Verhalten wie oben, und es macht dasselbe . Aber danke für den Punkt, an dem sich 'Iter' hier nennt. – ely