Variable defined with with-statement available outside of with-block?
Asked Answered
D

4

105

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.

Doering answered 21/6, 2011 at 21:42 Comment(2)
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: markus-gattol.name/ws/python.html#context_managerApophthegm
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.Lubricity
W
181

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.

Warily answered 21/6, 2011 at 23:23 Comment(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.Doering
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.Extricate
but how can I avoid this? like I made a context manager and don't want user to access the variable once the with clause endsVillalba
H
32

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.

Homebody answered 21/6, 2011 at 21:50 Comment(2)
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.Warily
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.Lubricity
T
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.

Thiele answered 21/6, 2011 at 21:49 Comment(4)
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?Doering
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.Doering
I searched, but haven't found anything significant for now. Interesting question, though.Thiele
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.Lubricity
C
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).

Canalize answered 21/6, 2011 at 23:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.