Block Scope in Python

Block scope in Python

No, there is no language support for creating block scope.

The following constructs create scope:

  • module
  • class
  • function (incl. lambda)
  • generator expression
  • comprehensions (dict, set, list(in Python 3.x))

Python scope, functions, blocks and non-local

In the first example you give, xfrs is only defined in the function you've provided as an example. We have to guess because you didn't provide a complete example, but the message from PyCharm suggests that you did not define this function inside another function that already had such an identifier defined in it and there's no xfrs in the global scope either.

In the second example, you assign to convert in the try section of a try .. except block. As .strptime() might fail with an exception, convert may end up not having been assigned a value, as you did not initialise convert outside the try block. So, PyCharm is correct (again, we have to assume as you did not provide a complete example.

Finally, in the third example, you start defining a function inside the function, but still apply nonlocal to a variable in the sixdig2iso() scope - it doesn't matter how many nestings of function you create inside this function, nonlocal only looks outward.

A typical use of nonlocal is as @Carcigenicate provides in their link (some modifications here):

x = 0

def outer():
x = 1

def inner():
nonlocal x
x += 1
return x

return x, inner

v, f = outer()
print(x, v, f())

The function returned by outer() produces 2 when called, as its non-local variable x of inner() is the same as the local variable x of outer(), starting at 1 and having 1 added as the function is called.

You can tell that is what happens, as the value returned by outer() is 1, but calling the returned function f() returns 2. All the time, the global x is untouched.

Try changing nonlocal to global and you'll find the the result changes from:

0 1 2

To:

0 1 1

I hope that helps explain what is going on with nonlocal.

How does scope work with try and except blocks in python?

You need something stronger than try to open a new scope, such as def or class. Your code has scoping rules similar to this version:

while True:

width = int(input("Please enter the width of your floor plan:\n "))
height = int(input("Please enter the height of your floor plan:\n "))
if width <= 0 or height <= 0:
print("You have entered and invalid character. Please enter characters only. Press enter to continue\n")
else:
print("Success!")
break

print(width)
print(height)

I assume that you're familiar with this scoping.

Implementing block scoping in Python

The locals variable in a frame cannot be written - it can be read, though.
The main reason for that is that although variables in any scope are representable as a dictionary, for efficiency at some point variables started being stored in a different way, in slots tied to the frame itself, but not visible from Python code. This way the operations for reading and writing a variable are not indirected through a dictionary, and there is a simple linear index for local variables that is used.

Whenever a call to locals() or the .f_locals member of a frame is accessed, cPython transparently copies the current values in the local variables to the resulting dictionary.

Furthermore, the other storage of local variable,s called "fast storage" implies that space is reserved for all local variables at compile time fr a code object - and functions are compiled to a single code object. That means that even if a variable is used only inside a block, it will exist function-wide.

This has been the behavior of Python for a long time, but it was somewhat recently documented in PEP 558: https://www.python.org/dev/peps/pep-0558/

For global variables there is no such mechanism, and affecting the dictionary returned by globals or frame.f_globals will change the corresponding variables - that is why your idea works for global vars.

The closest mechanism there is to that in the language is at the end of except blocks - at that point Python will delete the exception variable (to avoid having references to objects in the exception itself, which typically won't be reused after the except block.

If you want something that is actually useful, not just as a toy, one thing I've used 2 or 3 times is to have a context object that can be changed within a with block, and will restore its values upon exit - so that previous values are restored. This object is usually a "context" thing - for example, with parameters like foreground and linewidth for a drawing API, or number of decimal places and rounding strategies for Decimal numbers.

from collections import ChainMap

class Context:
def __init__(self):
self._stack = [{}]

def __enter__(self):
self._stack.append({})

def __exit__(self, *args, **kw):
self._stack.pop()

def __getattr__(self, name):
if name.startswith("_"):
return super().__getattr__(self, name)
return ChainMap(*reversed(self._stack))[name]

def __setattr__(self, name, value):
if name.startswith("_"):
return super().__setattr__(name, value)
self._stack[-1][name] = value

def __delattr__(self, name):
if name.startswith("_"):
return super().__deltattr__(name)
del self._stack[1][name]

And here is this code working in the an ipython console:


In [137]: ctx = Context()

In [138]: ctx.color = "red"

In [139]: ctx.color
Out[139]: 'red'

In [140]: with ctx:
...: ctx.color = "blue"
...: print(ctx.color)
...:
blue

In [141]: ctx.color
Out[141]: 'red'

(for a real good "production quality" Context class, you'd have to take in account thead safety and asyncio task-safety - but the above is the basic idea)

What are the differences (if exist) in variable scope between Python and C++?

Python doesn't provide a block scope. The minimum scope for a variable is the function (like it happens in pre-ES6 Javascript).

The original reason for this design (if I understood correctly) is that if you need block-scope variable then the code is probably too complex anyway and factorizing out a function is a good idea (note that you can create local functions/closures in Python so this doesn't necessarily mean the code will need to be spread and delocalized like it would happen with C).

As an exception to this "no-block-scope" rule, with Python3 variables used inside comprehensions were made local to the comprehension, i.e. after

x = [i*i for i in range(10)]

i will be 9 in Python2, but no variable i will leak from the expression in Python3.

Python rules for scoping are not very complex: if there are assignments (or augmented assigments like += -= and so on) in the body of the function then the variable is considered a local, if instead it's only accessed for reading the variable is considered coming from an outer scope (or a global if the function is at top level).

If you need to modify a global in a function (not something that should happen often) you need to declare it explicitly with global.

In Python3 it's also possible to access a local variable captured in an inner function for writing by using a nonlocal declaration. In Python2 instead captured local variables can only be accessed for reading in inner functions (assignment would make them locals of the inner function and global declaration would make them globals instead).

Scope of variable within with statement?

A with statement does not create a scope (like if, for and while do not create a scope either).

As a result, Python will analyze the code and see that you made an assignment in the with statement, and thus that will make the variable local (to the real scope).

In Python variables do not need initialization in all code paths: as a programmer, you are responsible to make sure that a variable is assigned before it is used. This can result in shorter code: say for instance you know for sure that a list contains at least one element, then you can assign in a for loop. In Java assignment in a for loop is not considered safe (since it is possible that the body of the loop is never executed).

Initialization before the with scope can be safer in the sense that after the with statement we can safely assume that the variable exists. If on the other hand the variable should be assigned in the with statement, not initializing it before the with statement actually results in an additional check: Python will error if somehow the assignment was skipped in the with statement.

A with statement is only used for context management purposes. It forces (by syntax) that the context you open in the with is closed at the end of the indentation.

python - Variable scope after using a 'with' statement

Yes, in Python the scope of a variable ends only when the code block it's defined in ends, and the with statement is not a code block per the documentation:

The following are blocks: a module, a function body, and a class
definition. Each command typed interactively is a block. A script file
(a file given as standard input to the interpreter or specified as a
command line argument to the interpreter) is a code block. A script
command (a command specified on the interpreter command line with the
‘-c’ option) is a code block. The string argument passed to the
built-in functions eval() and exec() is a code block.



Related Topics



Leave a reply



Submit