Python中的非局部变量

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python中的非局部变量相关的知识,希望对你有一定的参考价值。

参考技术A 非局部语句可以让所列的标识符(identifier)指向最近的嵌套作用域(enclosing scope)中已经绑定过的变量,全局变量除外。具体参考

b()中并不会赋值,a()和b()中i重名,报错local variable 'i' referenced before assignment

b()虽然可以读到a()中的变量i,但不会对其可写,nonlocal可以指定b()中i的赋值对象指定为a()中i

Python:如何允许“内部函数”更改多个“外部函数”中的非局部变量

【中文标题】Python:如何允许“内部函数”更改多个“外部函数”中的非局部变量【英文标题】:Python: How to allow ‘inner function’ to change nonlocal variables in multiple ‘outer functions’ 【发布时间】:2018-09-23 00:21:04 【问题描述】:

假设我有一个函数,其中有一大段代码在函数内的各个地方重复,我可以执行以下操作:

def foo():
    def bar():
        # do some stuff

    bar()
    # do some other stuff
    bar()

我可以在bar 内“读取”foo 范围内的变量,此外,如果我需要编辑它们,我可以这样做:

def foo():
    # stuff involving var1 and var2
    def bar():
        nonlocal var1, var2
        # do some stuff

    bar()
    # do some other stuff
    bar()

问题

现在假设我有几个函数,foo_1foo_2foo_3... 等等,所有这些函数都重复了来自 bar 的相同代码行。在每个foo_i 中定义bar 将是单调的(更不用说每次我想更改bar 时的噩梦),但是执行以下操作不起作用,因为它似乎nonlocal 适用于函数是在哪个定义的,而不是在哪个函数调用的:

def bar():
    nonlocal var1, var2  # SyntaxError: no binding for nonlocal 'var1' found
    # do some stuff

def foo_1():
    # stuff involving var1 and var2
    bar()
    # do some other stuff
    bar()

一个潜在的解决方案

解决此问题的一种方法是传入您需要更改的所有变量,然后返回它们。像这样的:

def bar(var1, var2):
    # do some stuff
    return var1, var2

def foo_1():
    # stuff involving var1 and var2
    var1, var2 = bar(var1, var2)
    # do some other stuff
    var1, var2 = bar(var1, var2)

我的问题

上面的解决方案有几个问题:

相当比简单的 bar() 更冗长(尤其是当有更多变量时) 实际上在每个foo_i 中定义bar 并没有太大的改进,因为假设我之前刚刚在bar 中访问了一个变量,我现在决定编辑。我不仅需要更改函数,而且在调用它的任何地方都需要更改(因为现在我必须返回一个额外的变量)。

有没有更好的方法来实现上述目标?

(这感觉像是一种无法回答的问题,所以如果这是一个重复的问题,我深表歉意。虽然我还没有找到任何东西。)

【问题讨论】:

简短回答:创建一个封装所有这些值的类。这将您必须传递的参数数量减少到 1。 (确保我理解正确)你的意思是类似于 namedTuple 吗?? 不,是可变的。 啊,是的。好点子。谢谢:) 这听起来像是一个有两个属性的对象。它调用改变其属性的方法(或返回具有更新属性的新实例)。具体来说,您想做什么? 【参考方案1】:

[...] 似乎 nonlocal 在定义函数的范围内起作用,而不是在其中调用它 [...]。

你是对的。 nonlocal 仅适用于定义所述函数的命名空间。来自nonlocal 的文档:

nonlocal 语句导致列出的标识符引用最近的封闭范围内的先前绑定的变量,不包括全局变量。

(强调我的)

正如@Aran 提到的,避免不必要的冗长的潜在解决方案是将要传递的变量包装到类中的某些函数中。 namedtuple 将是一个有吸引力的选择,因为您不需要完整的课程,但正如前面所述,它是不可变的。您可以改用 types.SimpleNamespace 对象,因为它们也是轻量级的:

from types import SimpleNamespace


def bar(n):
    n.a = 3, n.b = 4


def foo():
    n = SimpleNamespace(a=1, b=2)
    bar(n)
    print(n.a, n.b) # 3 4

【讨论】:

为这些变量编写一个自定义类可能很有意义。如果它们总是在函数之间一起传递,那意味着它们实际上应该是一个类。 (而不是 NameSpace 本质上只是一个使 dict 看起来像一个类的薄包装器。) 这是有道理的@Aran-Fey。但是,在我看来,将类用作命名空间很笨拙。是的,NameSpacedict 的 then 包装器,但能够使用 . 运算符而不是 [] 运算符 (IMO) 使得代码更具可读性。 是的,这是真的。但是,将这些foo_1foo_2 等函数实现为该类的方法可能是有意义的。如果不知道 OP 到底在做什么,就不可能说出来。只是把它作为建议扔出去。 嗯,好点@Aran-Fey。如果 OP 澄清了什么,我会更新。 谢谢你们的帮助:) 我只是想重构一些(相当长的)函数,以减少它们的重复性和更易于维护。通过类/字典/对象访问每个函数的所有变量将使代码更加冗长(我试图避免这种情况),尽管它确实很好地解决了每个地方的可维护性问题 bar 被调用。 【参考方案2】:

您可以在每个函数中修改它们之前将这些变量声明为global,但它们需要在任何使用它们的函数之前声明,还请记住,这将修改任何函数范围的变量它们被声明为全局:

var1 = var2 = None
def bar():
    global var1, var2
    # do some stuff

def foo_1():
    # stuff involving var1 and var2
    bar()
    # do some other stuff
    bar()

【讨论】:

以上是关于Python中的非局部变量的主要内容,如果未能解决你的问题,请参考以下文章

Python 2.x 中的 nonlocal 关键字

无法引用封闭范围中定义的非最终局部变量按钮,随机方法错误

python中的命名空间作用域全局变量与局部变量

Python中的全局变量与局部变量的区别

python 线程中的局部变量ThreadLocal

Java:访问其他类中的属性和局部变量问题