How can one create new scopes in python

Question:

In many languages (and places) there is a nice practice of creating local scopes by creating a block like this.

void foo()
{
     ... Do some stuff ...

     if(TRUE)
     {
         char a;
         int b;

         ... Do some more stuff ...
     }

     ... Do even more stuff ...
 }

How can I implement this in python without getting the unexpected indent error and without using some sort of if True: tricks

Asked By: Boris Gorelik

||

Answers:

Why do you want to create new scopes in python anyway?

The normal reason for doing it in other languages is variable scoping, but that doesn’t happen in python.

if True:
    a = 10

print a
Answered By: Douglas Leeder

variables in list comprehension (Python 3+) and generators are local:

>>> i = 0
>>> [i+1 for i in range(10)]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> i
0

but why exactly do you need this?

Answered By: SilentGhost

I would see this as a clear sign that it’s time to create a new function and refactor the code. I can see no reason to create a new scope like that. Any reason in mind?

Answered By: Jonas
def a():
    def b():
        pass
    b()

If I just want some extra indentation or am debugging, I’ll use if True:

Answered By: joeforker

In Python, scoping is of three types : global, local and class. You can create specialized ‘scope’ dictionaries to pass to exec / eval(). In addition you can use nested scopes
(defining a function within another). I found these to be sufficient in all my code.

As Douglas Leeder said already, the main reason to use it in other languages is variable scoping and that doesn’t really happen in Python. In addition, Python is the most readable language I have ever used. It would go against the grain of readability to do something like if-true tricks (Which you say you want to avoid). In that case, I think the best bet is to refactor your code into multiple functions, or use a single scope. I think that the available scopes in Python are sufficient to cover every eventuality, so local scoping shouldn’t really be necessary.

Answered By: batbrat

If you just want to create temp variables and let them be garbage collected right after using them, you can use

del varname

when you don’t want them anymore.

If its just for aesthetics, you could use comments or extra newlines, no extra indentation, though.

Answered By: XenF

A scope is a textual region of a
Python program where a namespace is
directly accessible. “Directly
accessible” here means that an
unqualified reference to a name
attempts to find the name in the
namespace…

Please, read the documentation and clarify your question.

btw, you don’t need if(TRUE){} in C, a simple {} is sufficient.

Answered By: jfs

Python has exactly two scopes, local and global. Variables that are used in a function are in local scope no matter what indentation level they were created at. Calling a nested function will have the effect that you’re looking for.

def foo():
  a = 1

  def bar():
    b = 2
    print a, b #will print "1 2"

  bar()

Still like everyone else, I have to ask you why you want to create a limited scope inside a function.

Answered By: David Locke

As mentioned in the other answers, there is no analogous functionality in Python to creating a new scope with a block, but when writing a script or a Jupyter Notebook, I often (ab)use classes to introduce new namespaces for similar effect. For example, in a notebook where you might have a model “Foo”, “Bar” etc. and related variables you might want to create a new scope to avoid having to reuse names like

model = FooModel()
optimizer = FooOptimizer()
...
model = BarModel()
optimizer = BarOptimizer()

or suffix names like

model_foo = ...
optimizer_foo = ...

model_bar = ...
optimizer_bar= ...

Instead you can introduce new namespaces with

class Foo:
    model = ...
    optimizer = ...
    loss = ....

class Bar:
    model = ...
    optimizer = ...
    loss = ...

and then access the variables as

Foo.model
Bar.optimizer
...

I find that using namespaces this way to create new scopes makes code more readable and less error-prone.

Answered By: Agost Biro

Like so, for arbitrary name t:

### at top of function / script / outer scope (maybe just big jupyter cell)
try: t
except NameError:
    class t
        pass
else:
    raise NameError('please `del t` first')

#### Cut here -- you only need 1x of the above -- example usage below ###

t.tempone = 5 # make new temporary variable that definitely doesn't bother anything else.
# block of calls here...
t.temptwo = 'bar' # another one...
del t.tempone # you can have overlapping scopes this way

# more calls
t.tempthree = t.temptwo; del t.temptwo # done with that now too
print(t.tempthree)
# etc, etc -- any number of variables will fit into t.


### At end of outer scope, to return `t` to being 'unused'
del t

All the above could be in a function def, or just anyplace outside defs along a script.

You can add or del new elements to an arbitrary-named class like that at any point. You really only need one of these — then manage your ‘temporary’ namespace as you like.

The del t statement isn’t necessary if this is in a function body, but if you include it, then you can copy/paste chunks of code far apart from each other and have them work how you expect (with different uses of ‘t’ being entirely separate, each use starting with the that try: t... block, and ending with del t).

This way if t had been used as a variable already, you’ll find out, and it doesn’t clobber t so you can find out what it was.

This is less error prone then using a series of random=named functions just to call them once — since it avoids having to deal with their names, or remembering to call them after their definition, especially if you have to reorder long code.

This basically does exactly what you want: Make a temporary place to put things you know for sure won’t collide with anything else, and which you are responsible for cleaning up inside as you go.

Yes, it’s ugly, and probably discouraged — you will be directed to decompose your work into a set of smaller, more reusable functions.

Answered By: RGD2

As others have suggested, the python way to execute code without polluting the enclosing namespace is to put it in a class or function. This presents a slight and usually harmless problem: defining the function puts its name in the enclosing namespace. If this causes harm to you, you can name your function using Python’s conventional temporary variable “_”:

def _():
    polluting_variable = foo()
    ...
_() # Run the code before something overwrites the variable.

This can be done recursively as each local definition masks the definition from the enclosing scope.

This sort of thing should only be needed in very specific circumstances. An example where it is useful is when using Databricks’ %run magic, which executes the contents of another notebook in the current notebook’s global scope. Wrapping the child notebook’s commands in temporary functions prevents them from polluting the global namespace.

Answered By: Tobias Hagge

While the leaking scope is indeed a feature that is often useful,
I have created a package to simulate block scoping (with selective leaking of your choice, typically to get the results out) anyway.

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/

Answered By: l74d
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.