如何解决模块中的全局变量?

时间:2013-02-12 18:07:55

标签: python module python-2.7 global-variables

我有一个脚本如下

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是什么,或者实例化的是什么clas。

2 个答案:

答案 0 :(得分:1)

eval之类的安全漏洞外,a中不能使用名称mapper.py,除非该名称在mapper.py中定义或从其他模块导入。无法让mapper.py自动无提示地从其他模块访问值a

此外,如果您在示例中仅在dict中使用它,则会在创建dict时立即评估a.foo。它不会等到你实际调用该函数;一旦评估a.foo来创建字典,它就会失败,因为它不知道a是什么。

你可以通过将元素包装在一个函数中来解决第二个问题(为了简洁起见使用lambda):

Mapper = {'test': lambda: a.foo}

。 。 。但除非你能以某种方式让amapper.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才能在其他模块中设置模块变量。

您可以通过使用Mapper的自定义类而不是常规字典来简化此操作,并让该类在其__getitem__中执行一些工作以查看“已知位置”(例如,读取一些模块变量)以用作评估a的基础。不过,这将是一个更重的解决方案。

最重要的是,您根本不能(再次,不使用eval或其他此类漏洞)在mapper.py中编写使用未定义变量a的代码,然后定义另一个模块中的变量a,并mapper.py自动知道该变量。某处某些代码必须“告诉”mapper.py您希望它使用a的值。

答案 1 :(得分:0)

我不确定我是否完全关注,但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']()