简体   繁体   中英

How to resolve a global variable in a module?

I have a script as follows

from mapper import Mapper

class A(object):
    def foo(self):
        print "world"

a = A()
a.foo()
Mapper['test']()

with Mapper defined in the file mapper.py :

Mapper = {'test': a.foo}

where I want to define a function call referencing an object not defined in mapper.py , but in the original code. However the code above gives the error

NameError: name 'a' is not defined

which makes kind of sense, as a is not defined in mapper.py itself. However, is it possible to change the code to let the code do the name resolution in the main code itself, or by the use of globals or something?

To solve this problem I could specify the implementation in mapper.py as a text and use eval in the main code, but I would like to avoid the usage of eval .

Additional information:

  • The full definition of the function has to be made in mapper.py
  • It is not known beforehand what the instance a is, or from what clas it is instantiated.

Barring security holes like eval , it's not possible to use a name a in mapper.py unless the name is either defined somewhere in mapper.py or imported from another module. There is no way to just let mapper.py automatically and silently access a value a from a different module.

In addition, if you're using it just in a dict as in your example, a.foo is going to be evaluated as soon as the dict is created. It's not going wait until you actually call the function; as soon as it evaluates a.foo to create the dict, it will fail because it doesn't know what a is.

You could get around this second problem by wrapping the element in a function (using a lambda for brevity):

Mapper = {'test': lambda: a.foo}

. . . but this still won't help unless you can somehow get a to be available inside mapper.py .

One possibility is to parameterize your Mapper by the "mystery" object and then pass that object in from outside:

# mapper.py
Mapper = {'test': lambda a: a.foo}

# other module
from mapper import Mapper
Mapper['test'](a)()

Or, similar to what mgilson suggested, you could "register" the object a with Mapper somehow. This lets you pass the object a only once to register it, and then you don't have to pass it for every call:

# mapper.py
Mapper = {'test': lambda a: Mapper['a'].foo}

# other module
from mapper import Mapper
Mapper['a'] = a
Mapper['test']()()

Note the two sets of parentheses at the end there: one set to evaluate the lambda and extract the function you want to call, and the second set to actually call that function. You could do a similar deal by, instead of using Mapper['a'] as the reference, using a module-level variable:

# mapper.py
Mapper = {'test': lambda: a.foo}

# other module
import mapper
Mapper = mapper.Mapper

mapper.a = a
Mapper['test']()()

Note that this requires you to do import mapper in order to set the module variable in that other module.

You could streamline this somewhat by using a custom class for Mapper instead of a regular dict, and having that class do some work in its __getitem__ to look in a "known location" (eg, read some module variable) to use as a base for evaluating a . That would be a heavier-weight solution though.

The bottom line is that you simply cannot (again, without the use of eval or other such holes) write code in mapper.py that uses an undefined variable a , and then define a variable a in another module and have mapper.py automatically know about that. There has to be some line of code somewhere that "tells" mapper.py what value of a you want it to use.

I'm not sure I completely follow, but a could "register" it's method with Mapper from anywhere which has a reference to Mapper:

#mapping.py
Mapper = {}

and then:

#main.py
from mapping import Mapper

#snip
a = A()
Mapper['test'] = a.foo #put your instance method into the Mapper dict.
#snip

Mapper['test']()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM