25

In a Python script, I encountered a variable that was defined inside a with statement, but that was used outside the statement, like file in the following example:

with open(fname, 'r') as file:
    pass
print(file.mode)

Intuitively I would say that file should not exist outside the with statement and that this only works by accident. I could not find a conclusive statement in the Python documentation on whether this should work or not though. Is this type of statement safe for use (also for future python versions), or should it be avoided? A pointer to this information in the Python docs would be very helpful as well.

Octaviour
  • 745
  • 6
  • 18
  • 2
    The code you posted shouldn't work. Leaving the context manager (the with block) will close the file. **I stand corrected. See comments below.** – Will Da Silva Jun 08 '17 at 13:55
  • @WillDaSilva have you tried it? It works for me in Python 2.7 – asongtoruin Jun 08 '17 at 13:56
  • 5
    It does work as `file.mode` does not access the file itself, just the Python object. – Octaviour Jun 08 '17 at 13:56
  • 3
    `with` blocks do not create a new scope. All references are created in the local scope. Same for loops etc., which is why after `for i in range(5):`, `i` is still available. The `with` block will `close` the file handle. – AChampion Jun 08 '17 at 13:58
  • 2
    yes, file will be closed. But object that represent closed file is still available, as `print()` call is in same scope as `with` statement. – Łukasz Rogalski Jun 08 '17 at 14:02

1 Answers1

22

Variable scope only applies at the function, module, and class levels. If you are in the same function/module/class, all variables defined will be available within that function/module/class, regardless of whether it was defined within a with, for, if, etc. block.

For example, this:

for x in range(1):
    y = 1
print(y)

is just as valid (although pointless) as your example using the with statement.

However, you must be careful since the variable defined within your code block might not actually be defined if the block is never entered, as in this case:

try:
    with open('filedoesnotexist', 'r') as file:
        pass
except:
    pass # just to emphasize point

print(file.mode)

Traceback (most recent call last):
  File "<pyshell#43>", line 1, in <module>
    file.mode
NameError: name 'file' is not defined

Good description of LEGB rule of thumb for variable scope

Graham
  • 7,431
  • 18
  • 59
  • 84
Billy
  • 5,179
  • 2
  • 27
  • 53