假设我有两个模块:
a.py
value = 3
def x()
return value
b.py
from a import x
value = 4
我的目标是在b
中使用a.x
的功能,但更改函数返回的值。具体来说,即使我运行b.x()
,value
也会以a
作为全局名称的来源进行查找。我基本上是在尝试在b.x
中创建函数对象的副本,该副本与a.x
相同,但使用b
来获取其全局变量。有没有一种相当直接的方法可以做到这一点?
下面是一个示例:
import a, b
print(a.x(), b.x())
结果目前是3 3
,但我希望它是3 4
的。
我想出了两种有效的复杂方法,但我对其中一种都不满意:
- 使用复制和粘贴在模块
b
中重新定义x
。实际函数比显示的要复杂得多,所以这不适合我。 定义一个可以传递给 x 的参数,只使用模块的值:
def x(value): return value
这给用户增加了我想要避免的负担,并没有真正解决问题。
有没有办法以某种方式修改函数获取其全局变量的位置?
我通过猜测和检查和研究的混合提出了一个解决方案。你几乎可以完全按照我在问题中提出的建议去做:复制一个函数对象并替换它的__globals__
属性。
我使用的是 Python 3,所以这里是上面链接的问题的答案的修改版本,并增加了一个覆盖全局变量的选项:
import copy
import types
import functools
def copy_func(f, globals=None, module=None):
"""Based on https://stackoverflow.com/a/13503277/2988730 (@unutbu)"""
if globals is None:
globals = f.__globals__
g = types.FunctionType(f.__code__, globals, name=f.__name__,
argdefs=f.__defaults__, closure=f.__closure__)
g = functools.update_wrapper(g, f)
if module is not None:
g.__module__ = module
g.__kwdefaults__ = copy.copy(f.__kwdefaults__)
return g
b.py
from a import x
value = 4
x = copy_func(x, globals(), __name__)
__globals__
属性是只读的,这就是为什么它必须传递给FunctionType
的构造函数。无法更改现有函数对象的__globals__
引用。
附言
我已经使用了足够多次,因为它在我编写和维护的实用程序库中实现,称为haggis
。请参阅haggis.objects.copy_func
。
所以我找到了一种方法来(有点)做到这一点,尽管我认为它不能完全解决你的问题。 使用 inspect,您可以访问调用函数的文件的全局变量。 因此,如果您像这样设置文件:
a.py
import inspect
value = 3
def a():
return inspect.stack()[1][0].f_globals['value']
b.py
from a import a
value = 5
print(a())
输出为 5,而不是 3。 但是,如果将这两个文件都导入到第三个文件中,它将查找第三个文件的全局变量。 只是想分享这个片段。
我遇到了同样的问题。但后来我想起eval
是一回事。
这是一个更短的版本(如果你不需要参数):
b.py:
from a import x as xx
# Define globals for the function here
glob = {'value': 4}
def x():
return eval(xx.__code__, glob)
希望 2 年后它仍然会有所帮助