使用修改后的副本隐藏全局变量

时间:2014-07-31 20:49:15

标签: python python-2.7 scope shadowing

请注意:这是有关如何更改函数体内全局变量的问题。我了解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()
  • 创建子类
  • 使用global中的modified_procedure()修改PARAMETER,然后在退回procedure()
  • 结果之前将其恢复

我可以通过遮蔽PARAMETER来实现吗?如果是这样,怎么样?

3 个答案:

答案 0 :(得分:4)

略微hackish,但你可以使用下面的方法使事情有效。

首先,定义初始方法如下:

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

答案 1 :(得分:1)

你要求的动态范围是多少。 Python,就像今天仍在使用的大多数语言一样(Perl和Emacs Lisp都是值得注意的例外)根本不支持动态范围,选择词法范围。

您可以更改procedure函数对象以替换其__globals__。但这会更加黑客,还有更多代码可以启动。如果您可以更改procedure以接受参数,请执行此操作。如果它必须涉及全球,那么暂时改变全球将是最不痛苦和令人惊讶的方法。

答案 2 :(得分:1)

你可以(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