Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modify namespace of importing script in Python

I'd like a function in my module to be able to access and change the local namespace of the script that's importing it. This would enable functions like this:

>>> import foo
>>> foo.set_a_to_three()
>>> a
3
>>>

Is this possible in Python?

like image 350
Luke Taylor Avatar asked Jul 03 '16 16:07

Luke Taylor


Video Answer


3 Answers

An answer was generously provided by @omz in a Slack team:

import inspect
def set_a_to_three():
    f = inspect.currentframe().f_back
    f.f_globals['a'] = 3

This provides the advantage over the __main__ solution that it works in multiple levels of imports, for example if a imports b which imports my module, foo, foo can modify b's globals, not just a's (I think)

However, if I understand correctly, modifying the local namespace is more complicated. As someone else pointed out:

It breaks when you call that function from within another function. Then the next namespace up the stack is a fake dict of locals, which you cannot write to. Well, you can, but writes are ignored.

If there's a more reliable solution, that'd be greatly appreciated.

like image 142
Luke Taylor Avatar answered Oct 10 '22 15:10

Luke Taylor


To let the module access your script's namespace, you will have to pass the namespace to the function. For example:

>>> import foo
>>> foo.set_a_to_three(globals(), locals())
>>> a
3

The inside of the code would look like this:

def set_a_to_three(globalDict, localDict):
    localDict['a'] = 3

Sidenote: In this case, the globals dictionary is not needed but in cases where you need to modify global variable, you will need to use the globals dictionary. I have included it here for this reason to make the function extensible.

However, it would be easier to just set a = 3 as Eli Sadoff said. It isn't a good practice to pass namespaces to other programs.

like image 3
u8y7541 Avatar answered Oct 10 '22 13:10

u8y7541


Disclaimer: This solution can only modify global variables, so it's not perfect. See Is it good practice to use import __main__? for pros and cons.

In foo.py:

import __main__

def set_a_to_three():
    __main__.a = 3

__main__ is a name given to the current top-level program. For example, if you import foo from a module bar, then bar can be referenced with the name __main__. If you import foo from bar, which was initially imported from a module qux, then qux is __main__, etc.

bar -> foo
bar is __main__

qux -> bar -> foo
qux is __main__

If you actually want to modify variables in bar rather than qux, for example because qux contains unit tests, you can use something like this:

import sys
import __main__
if "bar" in sys.modules:
    __main__ = sys.modules["bar"]
like image 3
fenceop Avatar answered Oct 10 '22 14:10

fenceop