Block scope in Python

When you code in other languages, you will sometimes create a block scope, like this:

statement
...
statement
{
    statement
    ...
    statement
}
statement
...
statement

One purpose (of many) is to improve code readability: to show that certain statements form a logical unit or that certain local variables are used only in that block.

Is there an idiomatic way of doing the same thing 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))

The idiomatic way in Python is to keep your functions short. If you think you need this, refactor your code! :)

Python creates a new scope for each module, class, function, generator expression, dict comprehension, set comprehension and in Python 3.x also for each list comprehension. Apart from these, there are no nested scopes inside of functions.


You can do something similar to a C++ block scope in Python by declaring a function inside your function and then immediately calling it. For example:

def my_func():
    shared_variable = calculate_thing()

    def do_first_thing():
        ... = shared_variable
    do_first_thing()

    def do_second_thing():
        foo(shared_variable)
        ...
    do_second_thing()

If you're not sure why you might want to do this then this video might convince you.

The basic principle is to scope everything as tightly as possible without introducing any 'garbage' (extra types/functions) into a wider scope than is absolutely required - Nothing else wants to use the do_first_thing() method for example so it shouldn't be scoped outside the calling function.


I agree that there is no block scope. But one place in Python 3 makes it seem as if it has block scope.

What happened which gave this look?

This was working properly in Python 2, but to make variable leakage stop in Python 3 they have done this trick and this change makes it look like as if it has block scope here.

Let me explain.


As per the idea of scope, when we introduce variables with same names inside the same scope, its value should be modified.

This is what is happening in Python 2:

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'W'

But in Python 3, even though the variable with same name is introduced, it does not override, and the list comprehension acts like a sandbox for some reason, and it seems like creating a new scope in it.

>>> x = 'OLD'
>>> sample = [x for x in 'NEW']
>>> x
'OLD'

And this answer goes against the answerer ThomasH's statement The only means to create scope is functions, classes or modules because this looks like one other place of creating a new scope.


I have come up with a solution with the easiest interface and the least amount of extra names to be introduced to your code.

from scoping import scoping
a = 2
with scoping():
    assert(2 == a)
    a = 3
    b = 4
    scoping.keep('b')
    assert(3 == a)
assert(2 == a)
assert(4 == b)

https://pypi.org/project/scoping/