【问题标题】:Override globals in function imported from another module覆盖从另一个模块导入的函数中的全局变量
【发布时间】:2018-03-02 20:07:16
【问题描述】:

假设我有两个模块:

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
    

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

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

【问题讨论】:

  • 你想要 C++ 中的返回引用之类的东西吗?
  • @liliscent 不确定那是什么,但我添加了一个我想要的示例。
  • 如果您只想修改另一个模块中的变量,只需使用a.value访问即可。我不知道你为什么提到那个函数x()
  • 因为我想在b 中复制引用b 的全局变量而不是a 的函数对象。
  • @liliscent。也许现在更清楚了?

标签: python global-variables


【解决方案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?

【讨论】:

  • @chrisz。我添加了一个选项,甚至可以伪造定义函数的模块
【解决方案2】:

所以我找到了一种方法来(某种程度上)做到这一点,尽管我认为它不能完全解决你的问题。使用检查,您可以访问调用您的函数的文件的全局变量。因此,如果您像这样设置文件:

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。但是,如果您将这两个都导入到第三个文件中,它将查找第三个文件的全局变量。只是想分享这个sn-p。

【讨论】:

  • 我发现了一些更直接的东西,但为此 def +1。
  • 介意分享你的想法吗?我以前遇到过类似的问题,对你的方法很感兴趣。
  • 我添加了一个答案。它比你所拥有的要长一点,但对于我的目的来说它更正确。也许它也对你有用。
【解决方案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 年后它仍然有用

【讨论】:

  • 这假设了一个比我想象的更具体的界面,但在某些情况下它确实有效。此外,这比平时更好地使用eval
猜你喜欢
  • 2013-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多