Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable global variable lookup in Python

In short, the question: Is there a way to prevent Python from looking up variables outside the current scope?

Details:

Python looks for variable definitions in outer scopes if they are not defined in the current scope. Thus, code like this is liable to break when not being careful during refactoring:

def line(x, a, b):     return a + x * b  a, b = 1, 1 y1 = line(1, a, b) y2 = line(1, 2, 3) 

If I renamed the function arguments, but forgot to rename them inside the function body, the code would still run:

def line(x, a0, b0):     return a + x * b  # not an error  a, b = 1, 1 y1 = line(1, a, b)  # correct result by coincidence y2 = line(1, 2, 3)  # wrong result 

I know it is bad practice to shadow names from outer scopes. But sometimes we do it anyway...

Is there a way to prevent Python from looking up variables outside the current scope? (So that accessing a or b raises an Error in the second example.)

like image 696
MB-F Avatar asked Jun 24 '15 09:06

MB-F


People also ask

How do you disable a variable in Python?

To delete a variable, it uses keyword “del”.

How do you stop global in Python?

You can assign the return value to variables in the calling scope. You can think of parameters as "knobs" that adjust the function's behavior. Inside the function, variables are just temporary storage used by the function needed to generate its one return value then disappear.

What is LEGB rule in Python?

The LEGB rule is a kind of name lookup procedure, which determines the order in which Python looks up names. For example, if you reference a given name, then Python will look that name up sequentially in the local, enclosing, global, and built-in scope. If the name exists, then you'll get the first occurrence of it.

Should you avoid using global variables in Python?

While in many or most other programming languages variables are treated as global if not declared otherwise, Python deals with variables the other way around. They are local, if not otherwise declared. The driving reason behind this approach is that global variables are generally bad practice and should be avoided.


2 Answers

Yes, maybe not in general. However you can do it with functions.

The thing you want to do is to have the function's global to be empty. You can't replace the globals and you don't want to modify it's content (becaus that would be just to get rid of global variables and functions).

However: you can create function objects in runtime. The constructor looks like types.FunctionType((code, globals[, name[, argdefs[, closure]]]). There you can replace the global namespace:

def line(x, a0, b0):    return a + x * b  # will be an error  a, b = 1, 1 y1 = line(1, a, b)  # correct result by coincidence  line = types.FunctionType(line.__code__, {}) y1 = line(1, a, b)  # fails since global name is not defined 

You can of course clean this up by defining your own decorator:

import types noglobal = lambda f: types.FunctionType(f.__code__, {}, argdefs=f.__defaults__)  @noglobal def f():     return x  x = 5 f() # will fail 

Strictly speaking you do not forbid it to access global variables, you just make the function believe there is no variables in global namespace. Actually you can also use this to emulate static variables since if it declares an variable to be global and assign to it it will end up in it's own sandbox of global namespace.

If you want to be able to access part of the global namespace then you'll need to populate the functions global sandbox with what you want it to see.

like image 79
skyking Avatar answered Oct 21 '22 08:10

skyking


No, you cannot tell Python not to look names up in the global scope.

If you could, you would not be able to use any other classes or functions defined in the module, no objects imported from other modules, nor could you use built-in names. Your function namespace becomes a desert devoid of almost everything it needs, and the only way out would be to import everything into the local namespace. For every single function in your module.

Rather than try to break global lookups, keep your global namespace clean. Don't add globals that you don't need to share with other scopes in the module. Use a main() function for example, to encapsulate what are really just locals.

Also, add unittesting. Refactoring without (even just a few) tests is always prone to create bugs otherwise.

like image 44
Martijn Pieters Avatar answered Oct 21 '22 09:10

Martijn Pieters