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
ais, or from what clas it is instantiated.
Barring security holes like
eval, it’s not possible to use a nameainmapper.pyunless the name is either defined somewhere inmapper.pyor imported from another module. There is no way to just letmapper.pyautomatically and silently access a valueafrom a different module.In addition, if you’re using it just in a dict as in your example,
a.foois 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 evaluatesa.footo create the dict, it will fail because it doesn’t know whatais.You could get around this second problem by wrapping the element in a function (using a lambda for brevity):
. . . but this still won’t help unless you can somehow get
ato be available insidemapper.py.One possibility is to parameterize your
Mapperby the “mystery” object and then pass that object in from outside:Or, similar to what
mgilsonsuggested, you could “register” the objectawithMappersomehow. This lets you pass the objectaonly once to register it, and then you don’t have to pass it for every call: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:Note that this requires you to do
import mapperin order to set the module variable in that other module.You could streamline this somewhat by using a custom class for
Mapperinstead of a regular dict, and having that class do some work in its__getitem__to look in a “known location” (e.g., read some module variable) to use as a base for evaluatinga. That would be a heavier-weight solution though.The bottom line is that you simply cannot (again, without the use of
evalor other such holes) write code inmapper.pythat uses an undefined variablea, and then define a variableain another module and havemapper.pyautomatically know about that. There has to be some line of code somewhere that “tells”mapper.pywhat value ofayou want it to use.