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

时间:2018-03-02 20:07:16

标签: python global-variables

假设我有两个模块:

a.py

value = 3
def x()
    return value

b.py

from a import x
value = 4

我的目标是在a.x中使用b的功能,但更改函数返回的值。具体而言,即使我运行value,也会使用a作为全局名称来查找b.x()。我基本上试图在b.x中创建一个与a.x相同但使用b获取其全局变量的函数对象的副本。有没有一种相当直接的方法呢?

以下是一个例子:

import a, b

print(a.x(), b.x())

结果目前为3 3,但我希望它为3 4

我提出了两种复杂的方法,但我对其中任何一种都不满意:

  1. 使用复制和粘贴在模块x中重新定义b。真正的功能比显示的要复杂得多,所以这不适合我。
  2. 定义一个可以传入x的参数,只需使用模块的值:

    def x(value):
        return value
    

    这增加了我想避免的用户负担,并没有真正解决问题。

  3. 有没有办法修改函数以某种方式获取其全局变量的位置?

3 个答案:

答案 0 :(得分:1)

所以我找到了一种方法(有点)这样做,虽然我不认为它完全解决了你的问题。使用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.但是,如果将这两个输入到第三个文件中,它将查找第三个文件的全局变量。只是想分享这个片段。

答案 1 :(得分:1)

我通过猜测和研究的混合物提出了一个解决方案。您可以完全按照我在问题中提出的内容执行操作:copy a function object并替换其__globals__属性。

我正在使用Python 3,所以这里是上面链接的问题answer的修改版本,添加了覆盖全局变量的选项:

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__引用:Why is func.__globals__ documented as read-only?

答案 2 :(得分:0)

我有同样的问题。但后来我想起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)

希望两年后仍然会有帮助