我有一个如下的脚本
from mapper import Mapper
class A(object):
def foo(self):
print "world"
a = A()
a.foo()
Mapper['test']()
其中Mapper
在文件mapper.py
:中定义
Mapper = {'test': a.foo}
其中我想定义一个函数调用,引用一个未在mapper.py
中定义但在原始代码中定义的对象。然而,上面的代码给出了错误
NameError: name 'a' is not defined
这是有意义的,因为a
没有在mapper.py
本身中定义。但是,是否可以更改代码,让代码在主代码中进行名称解析,或者使用globals
或其他方法?
为了解决这个问题,我可以将mapper.py
中的实现指定为文本,并在主代码中使用eval
,但我希望避免使用eval
。
附加信息:
- 必须在
mapper.py
中对函数进行完整定义 - 事先不知道实例
a
是什么,也不知道它是从什么类实例化的
除非eval
等安全漏洞存在,否则无法在mapper.py
中使用名称a
,除非该名称在mapper.py
中的某个位置定义或从另一个模块导入。不可能只让mapper.py
自动地、静默地访问来自不同模块的值a
。
此外,如果您只是在dict中使用它,就像您的示例中一样,那么一旦创建dict,就会对a.foo
进行评估。它不会等到你真正调用函数;一旦它评估a.foo
以创建dict,它就会失败,因为它不知道a
是什么。
您可以通过将元素封装在函数中(为了简洁起见,使用lambda)来解决第二个问题:
Mapper = {'test': lambda: a.foo}
但这仍然没有帮助,除非你能以某种方式让CCD_ 21在CCD_ 22中可用。
一种可能性是通过"神秘"对象参数化Mapper
,然后从外部传入该对象:
# mapper.py
Mapper = {'test': lambda a: a.foo}
# other module
from mapper import Mapper
Mapper['test'](a)()
或者,类似于mgilson
所建议的,您可以以某种方式用Mapper
"注册"对象a
。这样,您就可以只传递对象a
一次来注册它,然后不必每次调用都传递它:
# mapper.py
Mapper = {'test': lambda a: Mapper['a'].foo}
# other module
from mapper import Mapper
Mapper['a'] = a
Mapper['test']()()
请注意末尾的两组括号:一组用于计算lambda并提取要调用的函数,另一组用于实际调用该函数。您可以通过使用模块级变量来完成类似的操作,而不是使用Mapper['a']
作为参考
# mapper.py
Mapper = {'test': lambda: a.foo}
# other module
import mapper
Mapper = mapper.Mapper
mapper.a = a
Mapper['test']()()
请注意,这需要执行import mapper
,以便在其他模块中设置模块变量。
您可以通过使用Mapper
的自定义类而不是常规dict来简化这一点,并让该类在其__getitem__
中做一些工作,以查找"已知位置"(例如,读取一些模块变量),用作评估a
的基础。不过,这将是一个更重的解决方案。
最重要的是,您根本无法(同样,如果不使用eval
或其他此类漏洞)在使用未定义变量a
的mapper.py
中编写代码,然后在另一个模块中定义变量a
,并让mapper.py
自动知道这一点。必须在某个地方有一行代码"告诉"mapper.py
您希望它使用a
的值。
我不确定我是否完全遵循,但a
可以从任何引用Mapper:的地方用Mapper
"注册"它的方法
#mapping.py
Mapper = {}
然后:
#main.py
from mapping import Mapper
#snip
a = A()
Mapper['test'] = a.foo #put your instance method into the Mapper dict.
#snip
Mapper['test']()