How to access Globals() of parent module into a su

2019-07-21 00:24发布

问题:

I have a function in a sub-module which needs to manipulate the variables from parent/interpreter Globals (assertion, validation) like this:

import mymodule
mymodule.fun_using_main_interpreter_globals1()

If I do this, it works:

mymodule.fun_using_main_interpreter_globals1(explicit_pass= globals() )

But, If I dont pass explictely globals(), how can I get access to interpreter/parent globals() into my sub-module ?

In IPython, it can be put in profile settings.

回答1:

I never went for real in this territory, but looking at the documentation this should do it:

caller_globals = dict(inspect.getmembers(inspect.stack()[1][0]))["f_globals"]

inspect module allows you to access, among other things, the python interpreter stack. The second element (accessed with [1]) is the caller, the stack frame is the first element of the tuple (accessed with [0]) and it contains as member the current global dictionary for that context (named f_globals).

Note that this returns the globals() of the caller, not the one of the module of which the called function is a sub-module. That in general I think is not possible because the same module can be a sub-module of different modules (a sub-module is just a global in a module and it's possible that different modules share the same sub-module object).



回答2:

I had this working for local variables and came across this post while looking for how to get a caller context's global variables, too. While this topic had just enough information to help me see what I was missing ( thanks user @6502 ), I couldn't help but feel that if I didn't already have it working, I might have struggled a bit with the information that was here, therefore:

Here is a relatively simple way to access variables in a caller's context ( local or global ) that works in both py2 and py3:

import inspect
def furmat ( text ):
    # ... other initialization stuff ...
    this_frame = inspect.currentframe() # GET OUR FRAME HERE
    caller_frame = this_frame.f_back # GET CALLER'S FRAME HERE
    def caller_value ( key ):
        val = caller_frame.f_locals.get ( key ) # LOOKUP CALLER-LOCAL
        if val is not None:
            return val
        return caller_frame.f_globals[key] # LOOKUP CALLER-GLOBAL ( raise KeyError )
    # ... process text here, call caller_value() to query for variables in the caller's context
    del caller_frame # PREVENT MEMORY LEAKS PART 1
    del this_frame # AND PART 2

For those wanting to understand possible motivation, I built a format-like function called "furmat" that would automatically resolve any variables by name when surrounded by {} like py3's f"" strings, but that would work in py2. An example invocation looks like this:

from furmat import furmat
a = "AAA"
def test():
    b = "BBB"
    return furmat ( "{a}{b}" )
assert test() == "AAABBB"

Notice it resolves both the local variable {b} and the global variable {a}

I'm not posting the source code of furmat here only because it's 80 lines long and depends on a half-dozen other files in my private library, so it's a bit out of scope, but the snippet at the top of this post is the exact means that I'm using to lookup the values of any names provided within the format string ( minus most error handling to make the example more readable ).

In addition to the reason given above, I also wanted to build furmat because:

1) py2's unicode.format() throws a UnicodeError if you disable auto-conversion between bytes and unicode then try to take a {!r} because repr()'s output is always a bytes ( py2 str ) object so you can't avoid the automatic conversion,

2) I don't like how {!r} ambiguously only puts prints the b'' or u'' string prefix for non-native strings which confuses me when I frequently switch between py2 and py3 during mypy and unit testing, and

3) it was fun.