Skip to content
Advertisement

Method of class in module doesn’t see my globals()

I have a problem with globals using method of class from my testmodule

Example: Text of my test module:

cat ./testmodule.py

class testclass(object):
    def __init__(self):
        self.testfunc()
    
    def testfunc(self):
        print(' '.join(globals()).split(' '))

Text of my test class the same:

class testclass(object):
    def __init__(self):
        self.testfunc()
    
    def testfunc(self):
        print(' '.join(globals()).split(' '))

Text of my test func, nothing new:

def testfunc():
    print(' '.join(globals()).split(' '))

And go to test it.

Python 3.6.6 (default, Aug 13 2018, 18:24:23)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>>
>>> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa='a'
>>>
>>> import testmodule
>>>
>>> class testclass(object):
...     def __init__(self):
...         self.testfunc()
...
...     def testfunc(self):
...         print(' '.join(globals()).split(' '))
...
>>> def testfunc():
...     print(' '.join(globals()).split(' '))

Everything is ready, let’s test

>>> testfunc()
['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'testmodule', 'testclass', 'testfunc']

Variable exist

>>> testclass.testfunc(testclass)
['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__annotations__', '__builtins__', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'testmodule', 'testclass', 'testfunc']

The same result, variable exist

>>> testmodule.testclass.testfunc(testclass)
['__name__', '__doc__', '__package__', '__loader__', '__spec__', '__file__', '__cached__', '__builtins__', 'testclass']
>>>

Variable is lost.

How can i get the same result from testmodule like from testclass and testfunc?

Advertisement

Answer

Expanding on @chepner’s comment: globals() returns the module-level variables of the current module only.

  • When you define testfunc() in the REPL, it is defined as __main__.testfunc and invoking globals() returns essentially __main__.__dict__.
  • When defined in your testmodule, it is defined as testmodule.testfunc and globals() returns testmodule.__dict__.

If you want testfunction to access globals of another module, you need to ensure that globals() is called in the lexical scope of the module. The easiest was is to extend testfunction to expect a dictionary as argument:

## testmodule.py
def testfunc(globals_):
    print(globals)

## REPL or other module
import testmodule
testmodule.testfunc(globals())

Additional details

  • __main__ isn’t defined by default. You need to import __main__ before you can use it. But you can see that the current module is named __main__ from the variable __name__.
  • When you run python somescript.py, the contents of the file are executed as the __main__ module, not as a somescript module.
  • Python has closures, and functions inside modules behave essentially like closures over the module scope – so I would have expected globals() to be a member of every module. Then the following would have yielded the expected result:

    %% testmodule.py
    def testfunc(globals_):
        print(_globals())
    
    %% REPL
    import testmodule
    testmodule.testfunc(globals)
    

    but again, this returns the globals of only the testmodule. Instead globals is defined only in __builtin__, and seems to use the scope of the caller as hidden argument. As a consequence:

    ## REPL
    import testmodule
    testmodule.testfunc(globals) # prints testmodule.__dict__
    testmodule.testfunc(lambda: globals())  # prints __main__.__dict__
    

    Normally expect f and lambda: f() to have the same result.

  • You can’t rely on somemodule.__dict__ to be defined. A module may actually choose to return some wrapper object instead of a normal module object, when you import it. Actually, nothing even enforces that somemodule has typical module semantics! E.g. try the module:

    ## somemodule.py -- Please don't actually do this.
    import sys
    sys.modules["somemodule"] = "not a module"
    
    ## REPL
    >>> import somemodule
    >>> somemodule
    'not a module'
    

    A real-world example for such changes would be os.path:

    >>> import os.path
    >>> os.path
    <module 'ntpath' from 'C:toolsmsys64mingw64libpython2.7ntpath.pyc'>
    
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement