【问题标题】:Shadow a global variable with a modified copy使用修改后的副本隐藏全局变量
【发布时间】:2014-09-23 23:23:30
【问题描述】:

请注意:这不是关于如何更改函数体内的全局变量的问题。我了解global 关键字。

我的脚本有一堆全局配置变量。我想编写一个函数来隐藏本地命名空间中的一个全局变量(下面称为modified_procedure())并调用另一个引用配置变量的函数。即,

PARAMETER = 1

def procedure():
    return PARAMETER * 3

def modified_procedure():
    PARAMETER += 1
    return procedure()

这会失败,因为PARAMETER 出现在modified_procedure() 的主体中,所以解释器认为它是一个局部变量,而不是在全局命名空间中查找它。我不想改变全局变量PARAMETER;我试图在modified_procedure() 的命名空间中隐藏它。

我能想到几个不方便的解决方案:

  • OOPify 脚本,使配置变量成为对象属性,我可以使用新的procedure() 创建一个子类
  • 使用modified_procedure()中的global修改PARAMETER,然后在返回procedure()的结果之前恢复它

我可以通过跟踪PARAMETER 来做到这一点吗?如果有,怎么做?

【问题讨论】:

  • 你可以试试nonlocal 而不是global。我会自己测试它,但我坚持使用非常旧的 Python 版本。
  • @TheSoundDefense 在你调用函数之前你会得到一个错误。遗憾的是,nonlocal 仅适用于非全局名称(即闭包)。
  • 我认为 Python 2 中没有 nonlocal 关键字。

标签: python python-2.7 scope shadowing


【解决方案1】:

有点老套,但您可以使用下面的方法来使事情顺利进行。

首先,定义initial方法如下:

PARAMETER = 1

def procedure(PARAMETER = PARAMETER):
    return PARAMETER * 3

现在,在第二种方法中,您可以使用globals() method 并使用修改后的PARAMETER 调用它:

def modified_procedure_v1():
    PARAMETER = globals()["PARAMETER"] + 1
    return procedure()

您甚至可以通过以下方式使用更新的 PARAMETER 值从修改后的方法中调用第一个方法:

def modified_procedure_v2():
    PARAMETER = globals()["PARAMETER"] + 1
    return procedure(PARAMETER)

因此,最终结果变为:

>>> modified_procedure_v1() #uses global PARAMETER
3
>>> modified_procedure_v2() #uses PARAMETER value local to this method
6

【讨论】:

  • 中间/第二个sn-p应该和最后/第三个有同样的效果吗?
  • @delnan 没有。中间的,你不用PARAMETER的更新值,最后一个,你用PARAMETER的更新值
  • 很好,因为周围的文字听起来很像,那是错误的。
  • @delnan 我已经编辑了答案,我意识到它之前实际上很模棱两可。感谢您的评论:)
  • 我不认为第一个 sn-p 是第二个工作所必需的,因为在作业右侧访问 globals() 而不是 PARAMETER 可以避免箭头。我选择了@delnan,因为它让我有了更深入的了解,但你的正是我需要的技巧。
【解决方案2】:

您要求的内容相当于动态范围。 Python 与今天仍在使用的大多数语言(值得注意的例外是 Perl 和 Emacs Lisp)一样,根本不支持动态作用域,而是选择词法作用域。

您可以更改procedure 函数对象以替换其__globals__。但这会更加hacky,并且要启动更多代码。如果您可以更改 procedure 以接受参数,请执行此操作。如果它必须引用全局,那么暂时更改全局将是最不痛苦和最令人惊讶的方法。

【讨论】:

  • 跑到这里终于弄清了动态和词法作用域是什么意思,很高兴你指出了我的困惑。
【解决方案3】:

您可以(ab)使用mock 库临时更改PARAMETER 的值,如procedure 的全局范围中记录的那样:

import mock

def modified_procedure():
    with mock.patch.dict(procedure.func_globals, PARAMETER=PARAMETER+1):
        return procedure()

在 Python 3 中,mockunittest 包而不是第三方库的一部分,它会是

from unittest import mock

def modified_procedure():
    with mock.patch.dict(procedure.__globals__, PARAMETER=PARAMETER+1):
        return procedure()

不使用mock,您可以尝试类似

def modified_procedure():
    # Support Python 2 and 3. 
    try:
       d = procedure.__globals__
    except AttributeError:
       d = procedure.func_globals
    # Ensure that procedure()'s globals are restored
    # even if it raises an exception.
    try:
       d['PARAMETER'] += 1
       return procedure()
    finally:
       d['PARAMETER' -= 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-23
    • 1970-01-01
    • 2012-11-26
    • 1970-01-01
    相关资源
    最近更新 更多