In Lua, global variables are stored in a table named _G
. You can add a metatable to _G
, such that when you lookup an undefined global value, a user defined function is called to provide a value.
In the below example, looking up any undefined variable returns the name of the undefined variable.
setmetatable ( _G, { __index = function ( t, k ) return k end } )
print ( foo ) -- prints the string "foo"
foo = 5
print ( foo ) -- prints 5
print ( bar ) -- prints the string "bar"
Is it possible to achieve the same effect in Python 3?
Updated answer from Dec 12, 2018:
Here is another (better?) way. Basically, we manually read()
, compile()
, and then exec()
the entire source file. When we call exec()
, we can pass in an alternative global dictionary. The entire file is read, compiled, and executed twice, but we do achieve the desired result.
def baz ():
global foo
print ( foo ) # should print the string 'foo'
foo = 5
print ( foo ) # should print 5
print ( bar ) # should print the string 'bar'
class CustomGlobals ( dict ):
def __getitem__ ( self, k ):
if k in self: return self .get ( k )
if hasattr ( self .get ( '__builtins__' ), k ):
# we raise KeyError to avoid clobbering builtins
raise KeyError
return k
def main ():
with open ( __file__, 'rb' ) as f:
source = f .read() # re-read __file__
code = compile ( source, __file__, 'exec' ) # re-compile __file__
g = CustomGlobals ( globals() )
g [ '__name__' ] = None # prevent infinite recursion
exec ( code, g ) # re-exec __file__ against g
g [ 'baz' ] () # call the re-complied baz function
if __name__ == '__main__': main()
The above approach is superior (IMO) because we do not need to nest code in a string, and because error messages will contain correct line numbers.
Original answer from Nov 29, 2018:
If the Python code in question comes from a string (rather than a standard .py
file), then you can exec()
the string and provide a custom global dictionary as follows:
code = '''
print ( foo ) # should print the string 'foo'
foo = 5
print ( foo ) # should print 5
print ( bar ) # should print the string 'bar'
'''
class CustomDict ( dict ):
def __init__ ( self, other ): super() .__init__ ( other )
def __getitem__ ( self, k ):
if k in self: return self .get ( k )
if hasattr ( self .get ( '__builtins__' ), k ):
# we raise KeyError to avoid clobbering builtins
raise KeyError
return k
exec ( code, None, CustomDict ( globals() ) )
As desired, the above outputs:
foo
5
bar
I have not found any way to "mutate" a module to achieve the same result for code that is in that module. If anyone knows of a way to mutate a module, I would be very happy to see it.
(Perhaps a module could read its own source code into a string, then compile that string in the context of a custom global dict
, and then inject the result into sys.modules
? In other words, the module would replace itself with a clone of itself, but with a different global dict
. Hmmm.)
Aside: There are ways to simulate __getitem__()
and __getattr__()
on a module. And as of Python 3.7, you can simply and directly define a __getattr__()
function in the module. However, as far as I can tell, none of these techniques can hook into the lookup of global variables. Sources: