ZengJuchen
ZengJuchen

Reputation: 4821

Why globals() inside a function can't update variables in interactive shell?

Today I wrote an "alias import function" for myself, because I need to write a script to do variable value checks for different python files.

# filename: zen_basic.py

import importlib
def from_module_import_alias(module_name, var_name, alias):
    """ equal to from module import a as b """
    agent = importlib.import_module(module_name)
    globals()[alias] = vars(agent)[var_name]

The weird thing is, if I start a Python interactive shell, I can't import things by using this function. But by using its content outside of the function, it works.

>>> from zen_basic import *
>>> module_name = 'autor'
>>> var_name = 'food'
>>> alias = 'fd'
>>> from_module_import_alias(moduele_name, var_name, alias)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'moduele_name' is not defined
>>> from_module_import_alias(module_name, var_name, alias)
>>> fd
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'fd' is not defined
>>> agent = importlib.import_module(module_name)
>>> globals()[alias] = vars(agent)[var_name]
>>> fd
'cake'
>>>

I did 3 more experiments after:

    • Do python -i test.py
    • import zen_basic in interactive shell
    • call from_module_import_alias function, failed.
    • Do python -i zen_basic.py
    • call from_module_import_alias function, success.
    • add code which import zen_basic and call the from_module_import_alias function to test.py
    • Do python -i test.py, success

What's the reason that directly using from_module_import_alias function in a Python interactive shell failed?

Upvotes: 4

Views: 378

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1125368

You are updating the wrong globals. You are updating the globals of your zen_basic module, not the __main__ module (the namespace for your script or the interactive interpreter). The globals() function always returns the globals of the module in which the code was defined, not the module that called your function.

You'd have to retrieve the globals of the calling frame. Note that it rarely is advisable to modify the globals of the calling frame, but if you must then you can retrieve the calling frame by using the sys._getframe() function:

import sys

def from_module_import_alias(module_name, var_name, alias):
    """ equal to from module import a as b """
    agent = importlib.import_module(module_name)
    calling_globals = sys._getframe(1).f_globals
    calling_globals[alias] = vars(agent)[var_name]

In your experiments, python -i zen_basic.py runs zen_basic as the main script entry, and thus globals() references the __main__ module.

Upvotes: 6

Related Questions