Variable definiert mit with-Anweisung außerhalb von with-block verfügbar?

88

Betrachten Sie das folgende Beispiel:

with open('a.txt') as f:
    pass
# Is f supposed to be defined here?

Ich habe die Sprachdokumente (2.7) für With-Statement sowie PEP-343 gelesen, aber soweit ich das beurteilen kann, sagen sie nichts dazu.

In CPython fscheint 2.6.5 außerhalb des With -Blocks definiert zu sein, aber ich möchte mich lieber nicht auf ein Implementierungsdetail verlassen, das sich ändern könnte.

Heikki Toivonen
quelle
8
Die Frage, ob f im umschließenden Geltungsbereich verfügbar wäre oder nicht, wurde bereits beantwortet. Für mich hat das gesamte Konzept der Kontextmanager geklickt, als mir klar wurde, dass sich das Konzept eines Kontexts von dem des Umfangs unterscheidet . Hier ist ein Link zu meiner Website, der hoffentlich ein wenig hilft: markus-gattol.name/ws/python.html#context_manager
Tom
1
Genau - ein Kontext ist eine Frage der Änderung des aktuellen Status - Datei geöffnet, Datei geschlossen oder Thread gesperrt / entsperrt. Gerät zugewiesen / freigegeben. Alle im Gültigkeitsbereich genannten Variablen sind noch vorhanden. Sie verweisen jedoch jetzt auf freigegebene / geschlossene / entsperrte Handles.
Danny Staple

Antworten:

158

Ja, der Kontextmanager ist außerhalb der with-Anweisung verfügbar und dies ist nicht implementierungs- oder versionabhängig. Mit Anweisungen wird kein neuer Ausführungsbereich erstellt.

Fuzzyman
quelle
3
Dies ist meiner Meinung nach die klarste Erklärung, um die akzeptierte Antwort zu erhalten. gibt Alex und TokenMacGuy Punkte für zusätzliche hilfreiche Informationen.
Heikki Toivonen
Etwas, das man leicht vergessen könnte, wenn man eine Weile nicht mit Python gearbeitet hätte. Die Funktion wie Einrückung, Name und so weiter legt nahe, dass man nicht darauf zugreifen kann und es dennoch kann.
Vitaliy Terziev
28

die withSyntax:

with foo as bar:
    baz()

ist ungefähr Zucker für:

try:
    bar = foo.__enter__()
    baz()
finally:
    if foo.__exit__(*sys.exc_info()) and sys.exc_info():
        raise

Dies ist oft nützlich. Beispielsweise

import threading
with threading.Lock() as myLock:
    frob()

with myLock:
    frob_some_more()

Der Kontextmanager kann mehrmals von Nutzen sein.

SingleNegationElimination
quelle
Nun, die Wiederverwendung von Sperren kann oder kann nicht (keine Ahnung, aber es wäre ein Fehler, wenn sie unterschiedlich wären) - aber die Python-Scoping-Regeln werden hier bei allen Implementierungen definitiv gleich sein.
Fuzzyman
1
Dies ist wiederum kein Scoping-Problem. Der Umfang wird der gleiche sein. Wenn jedoch die Implementierung von foo .__ exit__ den Thread in einen gestoppten Zustand versetzt, sieht die zweite Anweisung nicht so aus, als würde sie den Thread-Sperren nützen, es sei denn, lock verfügt über eine Eingabe , die ihn erneut sperrt.
Danny Staple
16

Falls fes sich um eine Datei handelt, wird sie außerhalb der withAnweisung geschlossen angezeigt.

Zum Beispiel dies

f = 42
print f
with open('6432134.py') as f:
    print f
print f

würde drucken:

42
<open file '6432134.py', mode 'r' at 0x10050fb70>
<closed file '6432134.py', mode 'r' at 0x10050fb70>

Sie finden die Details in PEP-0343 im Abschnitt Spezifikation: Die ' with' -Anweisung . Python-Bereichsregeln (die möglicherweise irritierend sind ) gelten fauch für.

miku
quelle
Ich weiß das, ich habe es in der Frage erwähnt. Zumindest für CPython 2.6.5. Aber können Sie garantieren, dass dies auch für Jython, IronPython und PyPy gilt?
Heikki Toivonen
Die Bereichsregeln von Python sind auch nicht immer so klar. Beachten Sie dies in CPython 2.6.5 : [x for x in [1]]. xist außerhalb davon verfügbar. Machen Sie es zu einem Generator : (x for x in [1]). Jetzt xist nicht verfügbar. Ich erinnere mich, dass dies in Python 3 geändert wurde, so dass selbst bei Listenverständnis xkein Leck auftreten würde, aber ich kann die Referenz jetzt nicht finden.
Heikki Toivonen
Ich habe gesucht, aber bis jetzt nichts Bedeutendes gefunden. Interessante Frage.
Miku
Eigentlich ist dies keine Frage des Umfangs - die Variable f ist noch verfügbar, aber es ist jetzt ein Handle, das im geschlossenen Zustand abgelegt werden kann - dieselbe Datei, die zuvor geöffnet war. Der Exit-Aufruf, wenn der Kontext verlassen wird, ändert diesen Status.
Danny Staple
11

Um Heikkis Frage in den Kommentaren zu beantworten: Ja, dieses Scoping-Verhalten ist Teil der Python-Sprachspezifikation und funktioniert für alle kompatiblen Pythons (einschließlich PyPy, Jython und IronPython).

Alex Gaynor
quelle