覆盖从另一个模块导入的函数中的全局变量



假设我有两个模块:

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的。

我想出了两种有效的复杂方法,但我对其中一种都不满意:

  1. 使用复制和粘贴在模块b中重新定义x。实际函数比显示的要复杂得多,所以这不适合我。
  2. 定义一个可以传递给 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 年后它仍然会有所帮助

相关内容

  • 没有找到相关文章

最新更新