模块和变量范围

时间:2016-09-22 18:48:48

标签: python python-3.x

我不是python的专家,所以在我尝试理解变量范围的细微差别时请耐心等待。

作为描述我面临的问题的简单示例,请说我有以下三个文件。

第一个文件是outside_code.py。由于某些限制,我无法修改此文件。它必须按原样使用。它包含一些在某些时候运行eval的代码(是的,我知道eval是satan的产生,但是这是晚些时候的讨论)。例如,让我们说它包含以下代码行:

def eval_string(x):
    return eval(x)

第二个文件是一组用户定义的函数。我们称之为functions.py。它包含一些未知数量的函数定义,例如,让我们说functions.py包含一个函数,定义如下:

def foo(x):
    print("Your number is {}!".format(x))

现在我写了第三个文件,让它称之为main.py.其中包含以下代码:

import outside_code
from functions import *
outside_code.eval_string("foo(4)")

我使用*导入functions.py中的所有函数定义,因此main.py可以访问它们,而无需执行函数.foo()。我还导入了outside_code.py,因此我可以访问其核心功能,即包含eval的代码。最后,我在outside_code.py中调用该函数,传递一个与functions.py中定义的函数相关的字符串。

在简化示例中,我希望代码打印出来"您的号码是4!"。但是,我收到一条错误消息,指出' foo'没有定义。这显然意味着outside_code.py中的代码无法访问main.py中存在的相同foo函数。所以我需要让foo可以访问它。谁能告诉我foo目前的范围究竟是什么,以及如何扩展它以覆盖我实际想要使用它的空间?解决问题的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

foo已导入main.py;它的范围仅限于该文件(当然也包括最初定义的文件)。它在outside_code.py中不存在。

真正的eval函数接受locals和globals dicts,允许您将元素添加到评估代码的命名空间中。但是,如果您的eval_string尚未通过这些内容,则无法执行任何操作。

答案 1 :(得分:1)

相关文件:https://docs.python.org/3.5/library/functions.html#eval

eval使用可选字典将全局名称映射到值

eval('foo(4)', {'foo': foo})

会做你需要的。它将字符串'foo'映射到函数对象foo。

修改

重读您的问题,看起来这对您不起作用。我唯一的另一个想法是尝试

eval_str('eval("foo(4)", {"foo": lambda x: print("Your number is {}!".format(x))})')

但这是一个非常强硬的解决方案,并且无法很好地扩展到不适合lambda的函数。

答案 2 :(得分:1)

您必须将这些名称添加到outside_code的范围内。如果outside_code是常规Python模块,您可以直接执行此操作:

import outside_code
import functions

for name in getattr(functions, '__all__', (n for n in vars(functions) if not n[0] == '_')):
    setattr(outside_code, name, getattr(functions, name))

这会使所有名称functions导出(您使用from functions import *导入),并将对应对象的引用添加到outside_code,以便eval()outside_code.eval_string()eval_function() 1}}可以找到它们。

您可以使用ast.parse() function从表达式生成解析树,然后将其传递给outside_code,然后从表达式中提取所有全局名称,并仅添加这些名称至eval()来限制损害,可以这么说,但是你仍然会破坏其他模块命名空间以使其发挥作用。

请注意,这几乎与使用eval()一样邪恶,但如果你不能告诉其他模块中的eval()采用命名空间参数,那么它是你唯一的选择。那是因为默认eval_string()使用它作为命名空间运行的模块的全局命名空间。

但是,如果您的globals函数实际上接受了更多参数,请查找命名空间或def eval_string(x, namespace=None): return eval(x, globals=namespace) 选项。如果存在,该函数可能看起来更像这样:

outside_code.eval_string('foo(4)', vars(functions))

之后你可以这样做:

vars(functions)

其中functions为您提供fastlane screenshot模块的命名空间。