如何在模块中解析全局变量?
我有一个脚本,内容如下:
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
是什么,或者它是从哪个类实例化的。
2 个回答
我不太确定我完全理解了,不过a
可以在任何有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']()
除了像 eval
这样的安全漏洞之外,在 mapper.py
中使用名字 a
是不可能的,除非这个名字在 mapper.py
中定义过,或者从其他模块导入过。你不能让 mapper.py
自动、默默地从其他模块获取一个值 a
。
而且,如果你只是像你例子中那样在字典里使用它,a.foo
会在字典创建时立即被计算。它不会等到你真正调用这个函数时才计算;一旦它计算 a.foo
来创建字典,就会失败,因为它不知道 a
是什么。
你可以通过把这个元素放在一个函数里来解决第二个问题(用一个简短的 lambda 表达式):
Mapper = {'test': lambda: a.foo}
……但这仍然没用,除非你能以某种方式让 a
在 mapper.py
中可用。
一种可能的方法是通过“神秘”对象来参数化你的 Mapper
,然后从外部传入这个对象:
# mapper.py
Mapper = {'test': lambda a: a.foo}
# other module
from mapper import Mapper
Mapper['test'](a)()
或者,像 mgilson
建议的那样,你可以以某种方式将对象 a
“注册”到 Mapper
中。这样你只需传递一次对象 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
来设置模块变量。
你可以通过使用自定义类来代替普通字典来简化这个过程,让这个类在它的 __getitem__
方法中做一些工作,去一个“已知位置”(例如,读取某个模块变量)来作为评估 a
的基础。不过,这会是一个更复杂的解决方案。
总之,你不能(再次强调,除非使用 eval
或其他类似的漏洞)在 mapper.py
中写代码使用一个未定义的变量 a
,然后在另一个模块中定义这个变量 a
,让 mapper.py
自动知道这个变量。必须有某行代码在某个地方“告诉” mapper.py
你希望它使用哪个 a
的值。