Dies ist eher eine konzeptionelle Frage. Ich habe kürzlich in Python einen Code gesehen (der in 2.7 funktioniert hat und möglicherweise auch in 2.5 ausgeführt wurde), in dem eine for
Schleife denselben Namen sowohl für die Liste, über die iteriert wurde, als auch für das Element in der Liste verwendete , was mir sowohl als schlechte Praxis als auch als etwas erscheint, das überhaupt nicht funktionieren sollte.
Zum Beispiel:
x = [1,2,3,4,5]
for x in x:
print x
print x
Ausbeuten:
1
2
3
4
5
5
Jetzt ist es für mich sinnvoll, dass der zuletzt gedruckte Wert der letzte Wert ist, der x aus der Schleife zugewiesen wurde, aber ich verstehe nicht, warum Sie für beide Teile der for
Schleife denselben Variablennamen verwenden können und haben es funktioniert wie vorgesehen. Sind sie in verschiedenen Bereichen? Was ist unter der Haube los, damit so etwas funktioniert?
for i in printAndReturn [1,2,3,4,5] …
, wie oft soll[1,2,3,4,5]
gedruckt werden?for
Schleife haben also den gleichen Umfang.for ($x as $x)
, ist aber hässlicher Code IMOAntworten:
Was
dis
sagt uns:Python 3.4.1 (default, May 19 2014, 13:10:29) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> from dis import dis >>> dis("""x = [1,2,3,4,5] ... for x in x: ... print(x) ... print(x)""") 1 0 LOAD_CONST 0 (1) 3 LOAD_CONST 1 (2) 6 LOAD_CONST 2 (3) 9 LOAD_CONST 3 (4) 12 LOAD_CONST 4 (5) 15 BUILD_LIST 5 18 STORE_NAME 0 (x) 2 21 SETUP_LOOP 24 (to 48) 24 LOAD_NAME 0 (x) 27 GET_ITER >> 28 FOR_ITER 16 (to 47) 31 STORE_NAME 0 (x) 3 34 LOAD_NAME 1 (print) 37 LOAD_NAME 0 (x) 40 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 43 POP_TOP 44 JUMP_ABSOLUTE 28 >> 47 POP_BLOCK 4 >> 48 LOAD_NAME 1 (print) 51 LOAD_NAME 0 (x) 54 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 57 POP_TOP 58 LOAD_CONST 5 (None) 61 RETURN_VALUE
Die Schlüsselbits sind die Abschnitte 2 und 3 - wir laden den Wert aus
x
(24 LOAD_NAME 0 (x)
) und erhalten dann seinen Iterator (27 GET_ITER
) und beginnen, darüber zu iterieren (28 FOR_ITER
). Python kehrt nie wieder zurück, um den Iterator erneut zu laden .Abgesehen: Es würde keinen Sinn machen , dies zu tun, da sie bereits den Iterator hat, und wie Abhijit in seiner Antwort weist darauf hin , Abschnitt 7.3 der Python - Spezifikation erfordert tatsächlich dieses Verhalten).
Wenn der Name
x
überschrieben wird, um auf jeden Wert in der Liste zu verweisen, der früher alsx
Python bekannt war, hat er keine Probleme, den Iterator zu finden, da er den Namen nie wieder überprüfen muss, umx
das Iterationsprotokoll zu beenden.quelle
Verwenden Sie Ihren Beispielcode als Kernreferenz
x = [1,2,3,4,5] for x in x: print x print x
Ich möchte, dass Sie auf Abschnitt 7.3 verweisen . Die for-Anweisung im Handbuch
Auszug 1
Was es bedeutet, dass Ihre Variable
x
, die ein symbolischer Name eines Objekts istlist
:[1,2,3,4,5]
zu einem iterable Objekt ausgewertet wird. Selbst wenn die Variable, die symbolische Referenz, ihre Zugehörigkeit ändert, da die Ausdrucksliste nicht erneut ausgewertet wird, hat dies keine Auswirkungen auf das iterierbare Objekt, das bereits ausgewertet und generiert wurde.Hinweis
Auszug 2
Hier bezieht sich die Suite auf den Iterator und nicht auf die Ausdrucksliste. Daher wird der Iterator für jede Iteration ausgeführt, um das nächste Element zu erhalten, anstatt auf die ursprüngliche Ausdrucksliste zu verweisen.
quelle
Es ist notwendig, dass es so funktioniert, wenn Sie darüber nachdenken. Der Ausdruck für die Sequenz einer
for
Schleife kann alles sein:binaryfile = open("file", "rb") for byte in binaryfile.read(5): ...
Wir können die Sequenz nicht bei jedem Durchlauf durch die Schleife abfragen, oder hier würden wir beim zweiten Mal aus dem nächsten Stapel von 5 Bytes lesen . Natürlich muss Python das Ergebnis des Ausdrucks in irgendeiner Weise privat speichern, bevor die Schleife beginnt.
Nein. Um dies zu bestätigen, können Sie einen Verweis auf das ursprüngliche Bereichswörterbuch ( local () ) behalten und feststellen, dass Sie tatsächlich dieselben Variablen in der Schleife verwenden:
x = [1,2,3,4,5] loc = locals() for x in x: print locals() is loc # True print loc["x"] # 1 break
Sean Vieira hat genau gezeigt, was unter der Haube vor sich geht, aber um es in besser lesbarem Python-Code zu beschreiben, entspricht Ihre
for
Schleife im Wesentlichen dieserwhile
Schleife:it = iter(x) while True: try: x = it.next() except StopIteration: break print x
Dies unterscheidet sich von dem herkömmlichen Indizierungsansatz für die Iteration, den Sie in älteren Java-Versionen sehen würden, zum Beispiel:
for (int index = 0; index < x.length; index++) { x = x[index]; ... }
Dieser Ansatz schlägt fehl, wenn die Elementvariable und die Sequenzvariable identisch sind, da die Sequenz
x
nicht mehr verfügbar ist, um den nächsten Index nachzuschlagen, nachdem das erste Malx
dem ersten Element zugewiesen wurde.Beim ersteren Ansatz
it = iter(x)
fordert die erste Zeile ( ) jedoch ein Iteratorobjekt an, das tatsächlich für die Bereitstellung des nächsten Elements von da an verantwortlich ist. Auf die Sequenz, auf diex
ursprünglich verwiesen wurde, muss nicht mehr direkt zugegriffen werden.quelle
Es ist der Unterschied zwischen einer Variablen (x) und dem Objekt, auf das sie zeigt (die Liste). Wenn die for-Schleife startet, greift Python auf einen internen Verweis auf das Objekt zu, auf das x zeigt. Es verwendet das Objekt und nicht das, worauf x zu einem bestimmten Zeitpunkt verweist.
Wenn Sie x neu zuweisen, ändert sich die for-Schleife nicht. Wenn x auf ein veränderbares Objekt (z. B. eine Liste) zeigt und Sie dieses Objekt ändern (z. B. ein Element löschen), können die Ergebnisse unvorhersehbar sein.
quelle
Grundsätzlich nimmt die for - Schleife in der Liste
x
, und dann, dass die Speicherung als temporärer Variable, erneut Weist einex
an jeden Wert in diesen temporären Variablen. Somitx
ist jetzt der letzte Wert in der Liste.>>> x = [1, 2, 3] >>> [x for x in x] [1, 2, 3] >>> x 3 >>>
Genau wie in diesem:
>>> def foo(bar): ... return bar ... >>> x = [1, 2, 3] >>> for x in foo(x): ... print x ... 1 2 3 >>>
In diesem Beispiel
x
wird gespeichertfoo()
alsbar
, so dass , obwohlx
neu zugewiesen wird, es gibt sie noch (ED) infoo()
so dass wir es unsere auszulösen verwenden könntenfor
Schleife.quelle
x
es neu zugewiesen wird. Eine lokale Variablebar
wird in erstelltfoo
und der Wert von zugewiesenx
.foo
gibt dann diesen Wert in Form eines Objekts zurück, das in derfor
Bedingung verwendet wird. Daher wurde die Variablex
im zweiten Beispiel nie neu zugewiesen. Ich stimme dem ersten jedoch zu.x
ist jedoch immer noch die Iterationsvariable und nimmt daher für jede Schleife einen neuen Wert an. Nach der Schleifex
ist3
in beiden Fällen gleich.x
gilt3
undnot
[1,2,3] `?x
bezieht sich nicht mehr auf die ursprünglichex
Liste, daher gibt es keine Verwirrung. Grundsätzlich merkt sich Python, dass es über die ursprünglichex
Liste iteriert , aber sobald Sie anfangen, dem Namen den Iterationswert (0,1,2 usw.) zuzuweisen,x
verweist es nicht mehr auf die ursprünglichex
Liste. Der Name wird dem Iterationswert neu zugewiesen.In [1]: x = range(5) In [2]: x Out[2]: [0, 1, 2, 3, 4] In [3]: id(x) Out[3]: 4371091680 In [4]: for x in x: ...: print id(x), x ...: 140470424504688 0 140470424504664 1 140470424504640 2 140470424504616 3 140470424504592 4 In [5]: id(x) Out[5]: 140470424504592
quelle
x
hört einfach auf, auf die Bereichsliste zu verweisen, und erhält stattdessen die neuen Iterationswerte. Die Bereichsliste ist noch intakt. Wenn Sie sich den Wert vonx
nach der Schleife ansehen , wird es sein4
x
nie Bezug genommen wurdex
;x
auf eine Sequenz bezogen. Dann bezog es sich auf1
, dann auf2
usw.