it-swarm.com.de

Python seltsames Verhalten mit Aufzählung

Ich weiß, ich sollte die Liste nicht innerhalb einer Schleife ändern, aber aus Neugier möchte ich wissen, warum sich die Anzahl der Iterationen zwischen den beiden folgenden Beispielen unterscheidet.

Beispiel 1:

x = [1, 2, 3, 4, 5]
for i, s in enumerate(x):
    del x[0]
    print(i, s, x)

Beispiel 2

x = [1,2,3,4,5]
for i, s in enumerate(x):
    x = [1]
    print(i, s, x)

Beispiel 1 wird nur 3 mal ausgeführt, da bei i==3len(x)==2

Beispiel 2 wird 5 mal ausgeführt, obwohl len(x)==1.

Meine Frage ist also: Erzeugt enumerate eine vollständige Liste von (index, value)-Paaren am Anfang der Schleife und durchläuft sie? Oder werden sie bei jeder Wiederholung der Schleife generiert?

18
dbdq

enumerate () gibt einen Iterator oder ein anderes Objekt zurück, das die Iteration unterstützt. Die __next __ () - Methode des Iterators, die von enumerate () zurückgegeben wird, gibt einen Tuple zurück, der eine Anzahl (von start, deren Standardwert 0 ist) und die Werte enthält durch Iteration über iterable erhalten.

__next __ () gibt das nächste Element aus dem Container zurück. Wenn keine weiteren Elemente vorhanden sind, lösen Sie die Ausnahme StopIteration aus.

Erzeugt enumerate () am Anfang der Schleife eine vollständige Liste von (Index-, Wert-) Paaren und durchläuft diese? Oder werden sie bei jeder Wiederholung der Schleife generiert? 

So gibt enumerate() einen Iterator zurück und bei jeder Iteration überprüft __next__(), ob weitere Elemente vorhanden sind. enumerate() erstellt keine vollständige Liste am Anfang der Schleife. 

Wie in @Wisperwind erwähnt, weisen Sie in Ihrem zweiten Fall dem Namen x ein neues Objekt zu. Das Objekt, über das die Schleife wiederholt wird, ändert sich während der Iteration nicht.

18
Wasi Ahmad

Im ersten Beispiel ändern Sie tatsächlich die Liste, die Sie gerade durchlaufen.

Auf der anderen Seite weisen Sie dem Namen x nur ein neues Objekt zu. Das Objekt, das die Schleife durchläuft, ändert sich jedoch nicht.

Weitere Informationen zu Namen und Variablen in Python finden Sie unter http://foobarnbaz.com/2012/07/08/understanding-python-variables/ .

22
Wisperwind

Nur eine Klarstellung zu dem, was Wasi Ahmad und Wisperwind gesagt haben. Beide besagen, dass "Sie dem Namen x nur ein neues Objekt zuweisen". Dies kann etwas verwirrend sein, da es so interpretiert werden kann, als würde man sagen: "Sie erstellen ein neues Objekt ([1]) und speichern es unter dem Namen x, zu dem Sie sagen:" Nun ja, warum ändert sich das nicht ?! "Um zu sehen, was passiert, drucken Sie die ID des Objekts aus 

x = [1, 2, 3, 4, 5]
y = x  # To keep a reference to the original list
print id(x), id(y)
for i, v in enumerate(x):
    x = [1]
    print id(x), id(y)
print id(x), id(y)


# output (somewhat contrived as I don't have a python environment set up)
#    X ID            Y ID
10000000000001 10000000000001
10000000000002 10000000000001
10000000000003 10000000000001
10000000000004 10000000000001
10000000000005 10000000000001
10000000000006 10000000000001
10000000000006 10000000000001

Sie werden feststellen, dass sich id von x bei jedem Durchlauf der Schleife ändert. Wenn Sie mit der Schleife fertig sind, zeigt x auf die letzte in der Schleife vorgenommene Änderung. Wenn Sie Ihre Schleife durchlaufen, durchläuft sie die ursprüngliche Instanz von x, unabhängig davon, ob Sie sie noch referenzieren können. 

Wie Sie sehen, zeigt y auf die ursprüngliche x. Während Sie Ihre Iterationen durch die Schleife durchführen, zeigt x, obwohl y sich ändert, immer noch auf die ursprüngliche x, die immer noch überlaufen wird.

8
FuriousGeorge

In der Tat: Ihr erstes Snippet modifiziert die iterierte Liste an Ort und Stelle. Im zweiten wird die Variable x auf eine neue Liste gesetzt, wobei die von enumerate() durchbrochene Liste unverändert bleibt. Sie können dies in Aktion sehen, indem Sie die folgenden Links auf www.pythontutor.com aufrufen, über die Sie den Code in einem Schritt übergehen und den Inhalt Ihrer Variablen visualisieren können:

Um besser zu sehen, was los ist, gehen Sie nach hier , um stattdessen den folgenden erweiterten Code zu übergehen:

x = [1,2,3,4,5]
view = enumerate(x)
for i, s in view:
    x = [1]
    print(i, s, x)
1
alexis

Andere haben bereits darauf hingewiesen, dass Ihr zweites Beispiel nur den Wert ändert, auf den x zeigt, nicht jedoch die Liste, über die Sie iterieren. Dies ist ein perfektes Beispiel für den Unterschied zwischen gewöhnlicher Zuordnung (x = [1]) und Slice-Zuordnung (x[:] = [1]). Letzteres ändert die Liste x auf in-place :

x = [1, 2, 3, 4, 5]
for i, s in enumerate(x):
    x[:] = [1]
    print(i, s, x)

wird drucken

(0, 1, [1])
1
Florian Brucker
x = [1, 2, 3, 4, 5]

Die Liste [1, 2, 3, 4, 5] ist mit x 'gekennzeichnet.

for i, s in enumerate(x):

enumerate () hängt ein anderes Tag an, daher wird [1, 2, 3, 4, 5] jetzt mit x und y gekennzeichnet. Enumerate () verwendet weiterhin das Tag y, nicht das Tag x

del x[0]

Die im Speicher gespeicherte Liste wird geändert, daher beziehen sich x und y jetzt auf [2, 3, 4, 5].

Alternativ, wenn Sie verwenden

x = [1]

Eine neue Liste [1] wird im Speicher erstellt, und das Tag x zeigt jetzt darauf. Das Tag y zeigt immer noch auf die ursprüngliche Liste.

So funktionieren Python-Variablen:
http://foobarnbaz.com/2012/07/08/understanding-python-variables/

0
JeremiahBarrar