Reputation: 44325
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:
mapper.py
a
is, or from what clas it is instantiated. Upvotes: 4
Views: 3710
Reputation: 251378
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" (e.g., 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.
Upvotes: 1
Reputation: 309929
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']()
Upvotes: 0