How to force local scope in Python? - python

In C++, you can do this to force local scope:
{
int i = 1;
// Do stuff
}
// local variable i is destroyed
{
int i = 7;
// Do more stuff
}
This has the benefit that by the end of a forced local scope, any variables declared in the bracket are gone. This can help prevent using a previously defined variable x in a place later on where you didn't intend to use x.
Can you do this in Python? If so, how?
==UPDATE==
I'm aware of functions - which is the obvious thing. I was wondering if there was a quick way to do the above when the code is simple and not worth creating separate a function for - just some quick notation to emphasize that the variables in this block are not to be used anywhere else in the function.
From what people have said so far the short answer is no.
(I understand that there are clever ways like "del", or that this desire to have blocks may suggest refactoring into a separate function anyway. However I would like to emphasize this is just for short snippets where you want to emphasize the variables in this small block are not to be used elsewhere.)

In Python, if you declare a variable inside a function, it is local and cannot be accessed outside the function
>>> def x():
i = 5
>>> x()
>>> i
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
i
NameError: name 'i' is not defined
>>>
Alternatively, you can delete the variable from the namespace at the end so that you cannot reuse it.
>>> i = 5
>>> del i
>>> i
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
i
NameError: name 'i' is not defined
>>>

I had this same question, and found out that you absolutely can!
It's not as clean as the c style blocks, but through two quirks of python we can make it serve our purposes.
The Quirks:
Whatever code is inside a class runs immediately, even if the class is never used.
You can reuse the name of a class as many times as you want.
Here's your example:
class DoStuff:
i = 1
# Do stuff
# local variable i is destroyed
class DoStuff:
i = 7
# Do more stuff
# local variable i is destroyed
To fully represent the flexibility here, see this example. I've named the class "Scope", because that's probably what I'd call it to differentiate from other named classes.
Note that "Scope" can of course be anything.
I'd recommend you stick with one name for the entire project and add that name to your documentation, so that there is an understanding that this is a special name that should never ever be instantiated.
outer = 1
class Scope:
inner = outer
print("runs first ---")
print("outer %d" % outer)
print("inner %d" % inner)
class Scope:
inner = outer + 1
print("runs second ---")
print("outer %d" % outer)
print("inner %d" % inner)
print("runs last ---")
print("outer %d" % outer)
print("inner %d" % inner) # This will give an error. Inner does not exist in this scope!
Output:
runs first ---
outer 1
inner 1
runs second ---
outer 1
inner 2
runs last ---
outer 1
Traceback (most recent call last):
File "test.py", line 18, in <module>
print("inner %d" % inner) # This will give an error. Inner does not exist in this scope!
NameError: name 'inner' is not defined
So it is doable - let's take a look at the benefits / downsides tradeoffs.
Benefits:
Code remains linear and no unnecessary leaps in logic are needed to follow the code flow. This linearity will make it easier for newcomers to read and understand what a section of code actually does.
Code is self-documenting to future coders that this code is only used in this one place, making it easier to edit, as the coder will not need to do an unnecessary search to find other instances.
Downsides:
We're using quirks of Python to make this work, and I sense that this very idea of limiting scope as opposed to creating new one-time-use functions is not something that Python programmers tend to do. This may cause tensions in the workplace, or result in complaints of using a hack as opposed to following conventions on creating small functions whether or not something is used more than once.
If you leave the project and new programmers come onboard and see this code, they will probably be confused initially. Some documentation will be needed in order to set expectations, and care must be taken to make sure the explanation in the documentation remains accurate.
I think this is a worthwhile effort for all code where you'd like to limit the scope but there are not multiple places this code is used, or it is not yet clear how to write a generic function to address all those situations.
If anyone reading this feels there are other tradeoffs, comment here and I'll make sure they're represented in the "Downsides" section.
Here's some more discussion around this convention, which has been preferred by John Carmack, Jonathan Blow, and Casey Muratori.
https://news.ycombinator.com/item?id=12120752

I have committed to solve this with trickery.
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://github.com/l74d/scoping
By the way, I found that the dummy class solution might result in memory leak. For example, large numpy arrays created in the overwritten class did not seem to be garbage collected by watching the memory statistics, which may be an implementation-dependent thing though.

If you don't like the del solution, you can nest function definitions:
def one_function():
x=0
def f():
x = 1
f()
print(x) # 0
Of course, I think the better approach is to just split things up into smaller functions, so there's no need for this manual scoping. In C++, the coolest thing about it is that the destructor is automatically called -- in Python, you can't really guarantee that the destructor will be called, so this scoping wouldn't be very useful even if it were possible.

In C++, you use local scope with brackets {} to avoid variable redefinitions or naming conflicts:
{
int var=3;
}
{
float var=1.0f;
}
While in python, there are no explicit variable definition , you just assign some objects to a var name when you want to use it, and rebind the same name to some new variable:
var=3
#do something
var=1.0 #no need to "del var", var refers to a float object now
#do more stuff
Note that the use of scope block in C++ might be indicating your code needs to be refactored to functions or methods, which can be named and reused. And it's the same with python.

Related

Limit Python function scope to local variables only

Is there a way to limit function so that it would only have access to local variable and passed arguments?
For example, consider this code
a = 1
def my_fun(x):
print(x)
print(a)
my_fun(2)
Normally the output will be
2
1
However, I want to limit my_fun to local scope so that print(x) would work but throw an error on print(a). Is that possible?
I feel like I should preface this with: Do not actually do this.
You (sort of) can with functions, but you will also disable calls to all other global methods and variables, which I do not imagine you would like to do.
You can use the following decorator to have the function act like there are no variables in the global namespace:
import types
noglobal = lambda f: types.FunctionType(f.__code__, {})
And then call your function:
a = 1
#noglobal
def my_fun(x):
print(x)
print(a)
my_fun(2)
However this actually results in a different error than you want, it results in:
NameError: name 'print' is not defined
By not allowing globals to be used, you cannot use print() either.
Now, you could pass in the functions that you want to use as parameters, which would allow you to use them inside the function, but this is not a good approach and it is much better to just keep your globals clean.
a = 1
#noglobal
def my_fun(x, p):
p(x)
p(a)
my_fun(2, print)
Output:
2
NameError: name 'a' is not defined
Nope. The scoping rules are part of a language's basic definition. To change this, you'd have to alter the compiler to exclude items higher on the context stack, but still within the user space. You obviously don't want to limit all symbols outside the function's context, as you've used one in your example: the external function print. :-)

global variable not working python

Hello there developers,
i am writing code that takes the user input and initializes a class depending on the input like in the example code below:
class X:
def __init__(self):
return
def run(self):
print("i am X")
def func1(cls):
exec("global " + cls.lower())
exec(cls.lower() + " = " + cls + "()")
def func2(mode_to_set):
exec(mode_to_set.lower() + ".run()")
but as I run the code like this:
func1('X')
func2('X')
i keep getting this error:
Traceback (most recent call last):
File "/Users/noahchalifour/Desktop/test.py", line 16, in <module>
func2('X')
File "/Users/noahchalifour/Desktop/test.py", line 13, in func2
exec(mode_to_set.lower() + ".run()")
File "<string>", line 1, in <module>
NameError: name 'x' is not defined
Can anyone help me?
A much better way to instantiate a class based on user input would be to use a "factory pattern":
http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Factory.html
Basically you create a class whose whole purpose is to create other classes based on a value. Some people might find that overkill, so you could also use a function that creates classes based on input.
Whatever you do though, the way you have it now, running raw, user-input strings using exec, is a bad idea. The best case scenario is that it introduces new bugs that are near-impossible to trace since they aren't actually recorded anywhere. Worst case scenario, a user somehow finds a way to send a string to the function, you've pretty much destroyed whatever security you've hoped for.
Basically "exec" should generally be a last resort. There are usually more elegant and secure ways to solve the problem.
It seems like you'd be better off having func2 instantiate and run the method:
def func2(mode_to_set):
globals()[mode_to_set]().run()
In this way, you don't have a whole bunch of undesireable cruft floating about in your global namespace and you don't end up doing an untrusted exec. Also, execing a global statement inside a function doesn't work (as you've seen)... exec is a way to execute a string as if it were code. It isn't a way to drop dynamically created statements into the current function.
Dictionaries, dictionaries, dictionaries. Your program should maintain control over what code gets executed, rather than letting the user construct new code dynamically.
classes = {'X': X}
instances = {}
def func1(cls):
var = cls.lower()
instances[var] = classes[cls]()
def func2(mode_to_set):
instances[mode_to_set.lower()].run()
func1('X')
func2('X')
The only difference is that you don't have a global variable named x; you have a global dictionary with a key x that refers to your instance.

Is there an equivalent in Python of Fortran's "implicit none"?

In Fortran there is a statement Implicit none that throws a compilation error when a local variable is not declared but used. I understand that Python is a dynamically typed language and the scope of a variable may be determined at runtime.
But I would like to avoid certain unintended errors that happen when I forget to initialize a local variable but use it in the main code. For example, the variable x in the following code is global even though I did not intend that:
def test():
y=x+2 # intended this x to be a local variable but forgot
# x was not initialized
print y
x=3
test()
So my question is that: Is there any way to ensure all variables used in test() are local to it and that there are no side effects. I am using Python 2.7.x. In case there is a local variable, an error is printed.
So my question is that: Is there any way to ensure all variables used
in test() are local to it and that there are no side effects.
There is a technique to validate that globals aren't accessed.
Here's a decorator that scans a function's opcodes for a LOAD_GLOBAL.
import dis, sys, re, StringIO
def check_external(func):
'Validate that a function does not have global lookups'
saved_stdout = sys.stdout
sys.stdout = f = StringIO.StringIO()
try:
dis.dis(func)
result = f.getvalue()
finally:
sys.stdout = saved_stdout
externals = re.findall('^.*LOAD_GLOBAL.*$', result, re.MULTILINE)
if externals:
raise RuntimeError('Found globals: %r', externals)
return func
#check_external
def test():
y=x+2 # intended this x to be a local variable but forgot
# x was not initialized
print y
To make this practical, you will want a stop list of acceptable global references (i.e. modules). The technique can be extended to cover other opcodes such as STORE_GLOBAL and DELETE_GLOBAL.
All that said, I don't see straight-forward way to detect side-effects.
There is no implicit None in the sense you mean. Assignment will create a new variable, thus a typo might introduce a new name into your scope.
One way to get the effect you want is to use the following ugly-ish hack:
def no_globals(func):
if func.func_code.co_names:
raise TypeError(
'Function "%s" uses the following globals: %s' %
(func.__name__, ', '.join(func.func_code.co_names)))
return func
So when you declare your function test–with the no_globals wrapper–you'll get an error, like so:
>>> #no_globals
... def test():
... y = x + 2 # intended this x to be a local variable but forgot
... # x was not initialized
... print y
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in no_globals
TypeError: Function "test" uses the following globals: x
>>>
>>> x = 3
>>> test()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'test' is not defined
Just avoid using globally-scoped variables at all. And if you must, prefix their names with something you'll never use in a local variable name.
If you were really worried about this, you could try the following:
def test():
try:
x
except:
pass
else:
return
y = x+2
print y
But I'd recommend simply being mindful when writing a function that you don't try to reference things before assigning them. If possible, try to test each function separately, with a variety of carefully-defined inputs and intended outputs. There are a variety of testing suites and strategies, not to mention the simple assert keyword.
In Python, this is quite simply entirely legal. In fact, it is a strength of the language! This (lack) of error is the reason why you can do something like this:
def function1():
# stuff here
function2()
def function2():
pass
Whereas in C, you would need to "forward declare" function2.
There are static syntax checkers (like flake8) for Python that do plenty of work to catch errors and bad style, but this is not an error, and it is not caught by such a checker. Otherwise, something like this would be an error:
FILENAME = '/path/to/file'
HOSTNAME = 'example.com'
def main():
with open(FILENAME) as f:
f.write(HOSTNAME)
Or, something even more basic like this would be an error:
import sys
def main():
sys.stdout.write('blah')
The best thing you can do is use a different naming convention (like ALL_CAPS) for module level variable declarations. Also, make it a habit to put all of your code within a function (no module-level logic) in order to prevent variables from leaking into the global namespace.
Is there any way to ensure all variables used in test() are local to it and that there are no side effects.
No. The language offers no such functionality.
There is the built in locals() function. So you could write:
y = locals()['x'] + 2
but I cannot imagine anyone considering that to be an improvement.
To make sure the correct variable is used, you need to limit the scope of the lookup. Inside a function, Python will look to arguments defined in line, then to the args and kwargs. After those, its going to look outside the function. This can cause annoying bugs if the function depends on a global variable that gets changed elsewhere.
To avoid using a global variable by accident, you can define the function with a keyword argument for the variables your going to use:
def test(x=None):
y=x+2 # intended this x to be a local variable but forgot
# x was not initialized
print y
x=3
test()
I'm guessing you don't want to do this for lots of variables. However, it will stop the function from using globals.
Actually, even if you want to use a global variable in the function, I think its best to make it explicit:
x = 2
def test(x=x):
y=x+2 # intended this x to be a local variable but forgot
# x was not initialized
print y
x=3
test()
This example will use x=2 for the function no matter what happens to the global value of x afterwards. Inside the function, x is fixed to the value it had at compile time.
I started passing global variables as keyword arguments after getting burned a couple times. I think this is generally considered good practice?
The offered solutions are interesting, especially the one using dis.dis, but you are really thinking in the wrong direction. You don't want to write such a cumbersome code.
Are you afraid that you will reach a global accidentally? Then don't write globals. The purpose of module globals is mostly to be reached. (in a comment I have read that you have 50 globals in scope, which seems to me that you have some design errors).
If you still DO have to have globals, then either use a naming convention (UPPER_CASE is recommended for constants, which could cover your cases).
If a naming convention is not an option either, just put the functions you don't want to reach any global in a separate module, and do not define globals there. For instance, define pure_funcs and inside of that module, write your "pure" functions there, and then import this module. Since python has lexical scope, functions can only reach variables defined in outer scopes of the module they were written (and locals or built-ins, of course). Something like this:
# Define no globals here, just the functions (which are globals btw)
def pure1(arg1, arg2):
print x # This will raise an error, no way you can mix things up.

Understanding how Python "Compiles" or "Interprets" Function Objects

I have read the following posts but I am still unsure of something.
Python Compilation/Interpretation Process
Why python compile the source to bytecode before interpreting?
If I have a single Python file myfunctions.py containing the following code.
x = 3
def f():
print x
x = 2
Then, saying $ python myfunctions.py runs perfectly fine.
But now make one small change to the above file. The new file looks as shown below.
x = 3
def f():
print x
x = 2
f() # there is a function call now
This time, the code gives out an error. Now, I am trying to understand this behavior. And so far, these are my conclusions.
Python creates bytecode for x=3
It creates a function object f, quickly scans and has bytecode which talks about the local variables within f's scope but note that the bytecode for all statements in Python are unlikely to have been constructed.
Now, Python encounters a function call, it knows this function call is legitimate because the bare minimum bytecode talking about the function object f and its local variables is present.
Now the interpreter takes the charge of executing the bytecode but from the initial footprint it knows x is a local variable here and says - "Why are you printing before you assign?"
Can someone please comment on this? Thanks in advance. And sorry if this has been addressed before.
When the interpreter reads a function, for each "name" (variable) it encounters, the interpreter decides if that name is local or non-local. The criteria that is uses is pretty simple ... Is there an assignment statement anywhere in the body to that name (barring global statements)? e.g.:
def foo():
x = 3 # interpreter will tag `x` as a local variable since we assign to it here.
If there is an assignment statement to that name, then the name is tagged as "local", otherwise, it gets tagged as non-local.
Now, in your case, you try to print a variable which was tagged as local, but you do so before you've actually reached the critical assignment statement. Python looks for a local name, but doesn't find it so it raises the UnboundLocalError.
Python is very dynamic and allows you to do lots of crazy things which is part of what makes it so powerful. The downside of this is that it becomes very difficult to check for these errors unless you actually run the function -- In fact, python has made the decision to not check anything other than syntax until the function is run. This explains why you never see the exception until you actually call your function.
If you want python to tag the variable as global, you can do so with an explicit global1 statement:
x = 3
def foo():
global x
print x
x = 2
foo() # prints 3
print x # prints 2
1python3.x takes this concept even further an introduces the nonlocal keyword
mgilson got half of the answer.
The other half is that Python doesn't go looking for errors beyond syntax errors in functions (or function objects) it is not about to execute. So in the first case, since f() doesn't get called, the order-of-operations error isn't checked for.
In this respect, it is not like C and C++, which require everything to be fully declared up front. It's kind of like C++ templates, where errors in template code might not be found until the code is actually instantiated.

How can one create new scopes in python

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
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
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.
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.
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.
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?
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.
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.
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/
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?
def a():
def b():
pass
b()
If I just want some extra indentation or am debugging, I'll use if True:
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.
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.

Categories