Variable définie avec l'instruction with disponible en dehors de with-block?

88

Prenons l'exemple suivant:

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

J'ai lu la documentation du langage (2.7) pour with-statement ainsi que pour PEP-343, mais pour autant que je sache, ils ne disent rien à ce sujet.

Dans CPython 2.6.5 fsemble être défini en dehors du bloc with, mais je préfère ne pas me fier à un détail d'implémentation qui pourrait changer.

Heikki Toivonen
la source
8
La question de savoir si f serait disponible ou non dans le champ d'application englobant a déjà été résolue. Pour moi, tout le concept de gestionnaires de contexte a cliqué lorsque j'ai réalisé que le concept de contexte est différent de celui de portée . Voici un lien vers mon site Web qui, espérons-le, aide un peu: markus-gattol.name/ws/python.html#context_manager
Tom
1
Exactement - un contexte consiste à changer l'état actuel - fichier ouvert, fichier fermé ou thread verrouillé / déverrouillé. Appareil alloué / désalloué. Toutes les variables nommées dans la portée sont toujours là - mais elles pointeront désormais vers des poignées désallouées / fermées / déverrouillées.
Danny Staple

Réponses:

158

Oui, le gestionnaire de contexte sera disponible en dehors de l'instruction with et ne dépend pas de l'implémentation ou de la version. avec des instructions ne créent pas de nouvelle portée d'exécution.

fuzzyman
la source
3
C'est à mon avis l'explication la plus claire qui donne la réponse acceptée; donnera des points à Alex et TokenMacGuy pour des informations supplémentaires utiles.
Heikki Toivonen
Quelque chose que l'on pourrait facilement oublier si vous n'avez pas travaillé avec Python pendant un certain temps, la fonction comme l'indentation, le nom et d'autres choses suggère que vous ne devriez pas pouvoir y accéder et que vous le pouvez.
Vitaliy Terziev le
28

la withsyntaxe:

with foo as bar:
    baz()

est approximativement du sucre pour:

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

Ceci est souvent utile. Par exemple

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

with myLock:
    frob_some_more()

le gestionnaire de contexte peut être utilisé plus d'une fois.

SingleNegationElimination
la source
Eh bien, la réutilisation des verrous peut ou non (aucune idée, mais ce serait un bogue si elles étaient différentes) - mais les règles de portée Python seront certainement les mêmes ici dans toutes les implémentations.
fuzzyman
1
ce n'est pas encore un problème de portée. La portée sera la même. Cependant, si l'implémentation de foo .__ exit__ met le thread dans un état arrêté, alors à moins que lock n'ait une entrée qui le reverrouille, la seconde instruction ne semble pas faire quoi que ce soit d'utile pour les verrous de thread.
Danny Staple
16

Dans le cas où il fs'agit d'un fichier, il apparaîtra fermé en dehors de la withdéclaration.

Par exemple, ce

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

imprimerait:

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

Vous pouvez trouver les détails dans PEP-0343 sous la section Spécification: La déclaration «avec» . Les règles de portée Python (qui peuvent être irritantes ) s'appliquent fégalement.

Miku
la source
Je le sais, je l'ai mentionné dans la question. Pour CPython 2.6.5 au moins. Mais pouvez-vous garantir que cela vaut pour Jython, IronPython et PyPy?
Heikki Toivonen
Les règles de portée de Python ne sont pas toujours aussi claires non plus. Considérez ceci en CPython 2.6.5: [x for x in [1]]. xest disponible en dehors de cela. Faites - en un générateur: (x for x in [1]). Maintenant xn'est pas disponible. Je semble me souvenir que cela a été changé dans Python 3 afin que même avec la compréhension de la liste xne fuit pas, mais je ne peux pas trouver la référence maintenant.
Heikki Toivonen
J'ai cherché, mais je n'ai rien trouvé de significatif pour le moment. Question intéressante, cependant.
miku
En fait, ce n'est pas une question de portée - la variable f est toujours disponible, mais c'est maintenant un descripteur pour classer à l'état fermé - le même fichier qui était auparavant ouvert. L'appel de sortie lorsque le contexte est laissé changera cet état.
Danny Staple
11

Pour répondre à la question de Heikki dans les commentaires: oui, ce comportement de cadrage fait partie de la spécification du langage python et fonctionnera sur tous les Pythons compatibles (qui incluent PyPy, Jython et IronPython).

Alex Gaynor
la source