How do I 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 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 source code. However, the above code gives an error
NameError: name 'a' is not defined
which makes sense because a
it is not defined in itself mapper.py
. However, is it possible to change the code so that the code has name resolution in the main code itself or with help globals
or something else?
To solve this problem, I could specify the implementation in mapper.py
as text and use eval
in the main code, but I would like to avoid using eval
.
Additional Information:
- The complete function definition should be done in
mapper.py
- It is not known in advance what the instance is
a
, or from what it created.
source to share
Disallowing security holes, for example eval
, it is not possible to use a name a
in mapper.py
if the name is not defined somewhere in mapper.py
or imported from another module. There is no way to just allow mapper.py
automatic and silent access to a value a
from another module.
Also, if you use it just in a dict like in your example, it a.foo
will evaluate right after the dict is created. This will not wait until you actually call the function; once it evaluates a.foo
to create a dict it will fail because it doesn't know what it is a
.
You can work around this second problem by wrapping the element in a function (using lambda multiplicity):
Mapper = {'test': lambda: a.foo}
., but it still won't help if you can't somehow get a
to access internally mapper.py
.
One possibility is to parameterize yours with Mapper
a "mystery" object and then pass that object externally:
# mapper.py
Mapper = {'test': lambda a: a.foo}
# other module
from mapper import Mapper
Mapper['test'](a)()
Or, like the suggestion mgilson
, you could somehow "register" the object a
with Mapper
. This allows you to pass the object a
just 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']()()
Notice the two sets of parentheses at the end: one for evaluating the lambda and retrieving the function you want to call, and one for actually calling that function. You can do a similar deal, instead of using it Mapper['a']
as a 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 needs to be done import mapper
to set a modular variable in that other module.
You can simplify this procedure by using a special class for Mapper
instead of a regular dict, and have that class do some work in __getitem__
looking for a "known location" (eg, read some module variable) to use as a basis for evaluation a
. This would be a harder decision.
The bottom line is that you simply cannot (again, without using eval
or other such holes) write code in mapper.py
that uses the undefined variable a
, and then define the variable a
in another module and mapper.py
automatically know about it. There should be some line of code that "tells" mapper.py
which value a
you want to use.
source to share
I'm not sure if I'm completely following, but I a
can "register" its method using Mapper
from anywhere that has a link to the 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']()
source to share