Python 使用修改的副本对全局变量进行阴影处理

Python 使用修改的副本对全局变量进行阴影处理,python,python-2.7,scope,shadowing,Python,Python 2.7,Scope,Shadowing,请注意:这不是关于如何在函数体中更改全局变量的问题。我理解global关键字 我的脚本有一堆全局配置变量。我想编写一个函数,在本地名称空间中隐藏其中一个全局变量(下面称为modified_procedure()),并调用另一个引用配置变量的函数。即 PARAMETER = 1 def procedure(): return PARAMETER * 3 def modified_procedure(): PARAMETER += 1 return procedure()

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

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

PARAMETER = 1

def procedure():
    return PARAMETER * 3

def modified_procedure():
    PARAMETER += 1
    return procedure()
这会失败,因为
参数
出现在
modified_procedure()。我没有试图更改全局变量
参数
;我试图在
modified_procedure()
的命名空间中隐藏它

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

  • 对脚本进行OOP,使配置变量成为对象属性,我可以使用新的
    过程()创建一个子类
  • 使用
    modified_procedure()
    中的
    global
    修改
    参数
    ,然后在返回
    procedure()的结果之前将其还原

我可以通过阴影
参数
来完成吗?如果是这样的话,怎么做?

有点粗俗,但您可以使用下面的方法使事情顺利进行

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

PARAMETER = 1

def procedure(PARAMETER = PARAMETER):
    return PARAMETER * 3
现在,在第二种方法中,您可以使用并使用修改后的
参数调用它:

def modified_procedure_v1():
    PARAMETER = globals()["PARAMETER"] + 1
    return procedure()
您甚至可以通过以下方式使用更新的参数值从修改的方法中调用第一个方法:

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

你所要求的相当于动态范围界定。Python和今天仍然使用的大多数语言一样(Perl和Emacs Lisp是显著的例外),根本不支持动态范围,而是选择词法范围

您可以更改
过程
函数对象以替换其
\uuuuuu全局函数
。但这将是更黑客,更多的代码引导。如果可以更改
过程
以接受参数,请执行此操作。如果必须引用全局变量,临时更改全局变量将是最不痛苦和最令人吃惊的方法。

您可以(ab)使用
模拟
库临时更改
过程
全局范围中记录的
参数
的值:

import mock

def modified_procedure():
    with mock.patch.dict(procedure.func_globals, PARAMETER=PARAMETER+1):
        return procedure()
在Python3中,如果
mock
unittest
包的一部分,而不是第三方库,那么它应该是

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

您可以尝试
非本地
而不是
全局
。我会自己测试它,但我被一个非常旧的Python版本困住了。@TheSoundDefense在调用函数之前,你会得到一个错误<代码>非本地< /COD>只适用于非全局名称(即闭包)。在Python 2中,我不认为有一个<代码>非本地< /Cord>关键字。中间/第二个片段应该具有与最后一个/第三个?@ DelnA.No相同的效果,在中间一个,在最后一个参数中,您不使用参数的更新值。您使用PARAMETERGood的更新值,因为周围的文本使其听起来像是正确的,这是错误的。@delnan我编辑了答案,我意识到之前的答案实际上很模糊。感谢您的评论:)我不认为第一个代码片段对第二个代码片段起作用是必要的,因为访问赋值右侧的
globals()
而不是
PARAMETER
可以避免箭头。我选择@delnan's是因为它让我有了更深的理解,但你的是我需要的技巧。我终于明白了动态范围和词汇范围的含义,很高兴你指出了我的困惑。