3

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

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

PARAMETER = 1

def procedure():
    return PARAMETER * 3

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

这失败了,因为PARAMETER发生在的主体中,modified_procedure()所以解释器认为它是一个局部变量,而不是在全局命名空间中查找它。我不想改变全局变量PARAMETER;我正在尝试将其隐藏在modified_procedure()的名称空间中。

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

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

我可以通过阴影来做到这一点PARAMETER吗?如果是这样,怎么做?

4

3 回答 3

4

有点骇人听闻,但您可以使用下面的方法来使事情正常进行。

首先,定义initial方法如下:

PARAMETER = 1

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

现在,在第二种方法中,您可以使用该globals()方法并使用修改后的方法调用它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
于 2014-07-31T20:56:02.000 回答
1

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

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

于 2014-07-31T20:56:32.617 回答
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
于 2014-07-31T21:05:13.863 回答