103

Consider the following example:

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

I have read the language docs (2.7) for with-statement as well as PEP-343, but as far as I can tell they don't say anything on this matter.

In CPython 2.6.5 f does seem to be defined outside of the with-block, but I'd rather not rely on an implementation detail that could change.

Heikki Toivonen
  • 30,964
  • 11
  • 42
  • 44
  • 8
    The question of whether or not f would be available in the enclosing scope has already been answered. For me the whole concept of context managers clicked when I realized that the concept of a *context* is different to that of *scope*. Here is a link to my website that hopefully helps a little: http://www.markus-gattol.name/ws/python.html#context_manager – evdama Jun 22 '11 at 08:36
  • 1
    Exactly - a context is a matter of changing the current state - file open, file closed or thread locked/unlocked. Device allocated/deallocated. All the variables named in scope are still there - but they will now point to deallocated/closed/unlocked handles. – Danny Staple Feb 05 '13 at 14:41

4 Answers4

177

Yes, the context manager will be available outside the with statement and that is not implementation or version dependent. with statements do not create a new execution scope.

fuzzyman
  • 8,271
  • 2
  • 30
  • 32
  • 3
    This is the clearest explanation in my opinion so awarding the accepted answer; will give points to Alex and TokenMacGuy for additional helpful info. – Heikki Toivonen Jun 22 '11 at 18:58
  • Something one could easily forget if have not worked with Python for a while, the function like indentation, name and stuff suggests that you should not be able to access it and yet you can. – Vitaliy Terziev Jan 17 '19 at 11:57
33

the with syntax:

with foo as bar:
    baz()

is approximately sugar for:

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

This is often useful. For example

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

with myLock:
    frob_some_more()

the context manager may be of use more than once.

Neil G
  • 32,138
  • 39
  • 156
  • 257
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
  • Well, lock reuse may or may not (no idea, but it would be a bug if they were different) - but the Python scoping rules will definitely be the same here across implementations. – fuzzyman Jun 21 '11 at 23:21
  • 1
    this is again not a scoping issue. The scoping will be the same. However, if the implementation of foo.__exit__ puts the thread into a stopped state, then unless lock has an __enter__ that relocks it, the second statement doesn't look like it would do anything useful to the thread locks. – Danny Staple Feb 05 '13 at 14:40
18

In case f is a file, it will be appear closed outside the with statement.

For example, this

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

would print:

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

You can find the details in PEP-0343 under the section Specification: The 'with' Statement. Python scope rules (which might be irritating) apply to f as well.

miku
  • 181,842
  • 47
  • 306
  • 310
  • I know this, I mentioned it in the question. For CPython 2.6.5 at least. But can you guarantee that this same holds for Jython, IronPython and PyPy? – Heikki Toivonen Jun 21 '11 at 21:57
  • Python's scope rules are not always so clear either. Consider this in CPython 2.6.5: `[x for x in [1]]`. `x` is available outside of that. Make it into a generator: `(x for x in [1])`. Now `x` is not available. I seem to recall this got changed in Python 3 so that even with list comprehension `x` would not leak, but I can't find the reference now. – Heikki Toivonen Jun 21 '11 at 22:20
  • I searched, but haven't found anything significant for now. Interesting question, though. – miku Jun 21 '11 at 22:24
  • Actually this is not a scope matter - the variable f is still available, but it is now a handle to file in the closed state - the same file that was previously open before. The exit call when the context is left will change this state. – Danny Staple Feb 05 '13 at 14:39
13

To answer Heikki's question in the comments: yes, this scoping behavior is part of the python language specification and will work on any and all compliant Pythons (which includes PyPy, Jython, and IronPython).

Alex Gaynor
  • 14,353
  • 9
  • 63
  • 113