使用类作为局部变量的容器对象作为';非本地';用Python

使用类作为局部变量的容器对象作为';非本地';用Python,python,python-3.x,Python,Python 3.x,问题 在Python中,如果我有一个带有局部变量和嵌套函数的函数,我可以借助非局部变量在嵌套函数中分配这些变量: def f(): x = 0 def _g(): nonlocal x x = 1 def _h(): x = 2 _g() _h() print(x) f() # 1 这个解决方案的问题是,如果我在f()中有许多局部变量,那么使用非局部变量是冗长的,更令人不安的是,很容易忘记变量的非局

问题

在Python中,如果我有一个带有局部变量和嵌套函数的函数,我可以借助非局部变量在嵌套函数中分配这些变量:

def f():
    x = 0
    def _g():
        nonlocal x
        x = 1
    def _h():
        x = 2
    _g()
    _h()
    print(x)
f() # 1
这个解决方案的问题是,如果我在f()中有许多局部变量,那么使用非局部变量是冗长的,更令人不安的是,很容易忘记变量的非局部性,并且在嵌套函数中创建局部变量而不注意它(例如,我在上面的h()函数中真正的意思是什么?)

我看到并使用了另一种选择:

def f():
    class state:
        x = 0
    def _g():
        state.x = 1
    def _h():
        x = 2
    _g()
    _h()
    print(state.x)
f() # 1
If使用了一个事实,即Python中的类实际上也是一个对象。以这种方式使用“类”实际上是为本地值创建可变容器的最简单的方式(我相信)

我认为这种模式有两个值得怀疑的方面:

  • “class”关键字的这种特殊用法可能被认为是一种黑客行为
  • 由于容器有些人为,有时很难为它找到一个好名字(我甚至测试了“self”这个名字的用法,但这看起来更像是一个黑客)
这是一个好的模式吗?你通常使用什么替代方案

如果StackOverflow不是解决此问题的合适论坛,请在您认为更适合的其他论坛上提供建议(我确实阅读了常见问题解答,但我并不清楚该问题的正确论坛是什么)

提前谢谢

附言

为了完整起见,至少还有一种选择,感觉更像是黑客:

class f:
    def __init__(self):
        self.x = 0
        self._g()
        self._h()
        print(self.x)
    def _g(self):
        self.x = 1
    def _h(self):
        x = 2
f() # 1
这是因为在Python中,类实例化与函数调用具有相同的语法

回答


请参阅下面的公认答案。有关函数需要返回值时解决方案的讨论,请参见。

为什么不使用
dict
而不是类。这消除了一些“hackynes”,并且与Python 2兼容:

def f():
    state=dict(x=0)

    def _g():
        state["x"] = 1

    def _h():
        x = 2
    _g()
    _h()
    print(state["x"])
f() # 1

为什么不直接使用
dict
而不是类呢。这消除了一些“hackynes”,并且与Python 2兼容:

def f():
    state=dict(x=0)

    def _g():
        state["x"] = 1

    def _h():
        x = 2
    _g()
    _h()
    print(state["x"])
f() # 1
您的“最后手段”alernative当然更干净——如果您可以在共享一个状态的扁平化方法中执行此操作,那么与执行相同任务的嵌套函数相比,这是更可读的代码。“扁平比嵌套好”

除此之外,您正在创建一个具有名称空间的类。您可以简单地创建一个对象作为名称空间使用,它将作为名称空间使用——这更常见。唯一的问题是,如果您只是创建
对象
本身的实例,它就不能作为名称空间使用,因为它没有
\uuuuuuu dict\uuuuuu
,因此您不能自由地为该对象赋予属性

这就是为什么stdlib中的
types
模块中有一个名为
SimpleNamespace
的类正是针对这个用例的

只要做:

from types import SimpleNamespace

def f():
    state = SimpleNamespace()
    state.x = 0
    ...
(但前提是你不改变主意,选择更干净的基于类的解决方案)。

你的“最后一招”alernative当然更干净——如果你能在共享状态的扁平方法中实现这一点,那么代码的可读性比嵌套函数更高。“扁平比嵌套好”

除此之外,您正在创建一个具有名称空间的类。您可以简单地创建一个对象作为名称空间使用,它将作为名称空间使用——这更常见。唯一的问题是,如果您只是创建
对象
本身的实例,它就不能作为名称空间使用,因为它没有
\uuuuuuu dict\uuuuuu
,因此您不能自由地为该对象赋予属性

这就是为什么stdlib中的
types
模块中有一个名为
SimpleNamespace
的类正是针对这个用例的

只要做:

from types import SimpleNamespace

def f():
    state = SimpleNamespace()
    state.x = 0
    ...

(但前提是你不改变主意,选择更干净的基于类的解决方案)。

因为冗长。输入3个额外的符号,然后读回-字典不是“干净的”。如果您使用的是国际键盘,通常
会映射为死信,然后必须按空格键-除了名称空间变量外,还有整整6次按键才能到达
x
。因为冗长。输入3个额外的符号,然后读回-字典不是“干净的”。如果您使用的是国际键盘,通常
会映射为死信,然后必须按空格键-除了名称空间变量外,还有整整6次按键才能到达
x
。因此您认为实例化一个类是为了它的_uinit_u()的副作用方法和扔掉产生的对象是一个好的模式?我没听错吧?显然你是这个意思。我只是没想到会有这样的答案,因为其他一些有经验的Stackoverflower在前面的问题中称之为黑客攻击。但为什么不呢?毕竟,这会创建一个对象,因为它是必需的,并在完成任务后将其丢弃。由于对象创建有函数调用语法,我们使用函数大小写。伟大的我相信这个模式也可以用C++来完成。你的关于SimpleNameSpace的信息也很有趣。谢谢。“实例化对象”不是编写Python代码时应该考虑的效率问题。你看,即使是交换两个变量的“酷”模式:
a,b=b,a
也会创建一个元组并将其销毁。到目前为止,使用嵌套函数在可读性方面遇到的困难并不值得。它只是
类SimpleNamespace(object):pass
的“stdlib糖”。因此,您认为实例化一个类以消除其u init_u()方法的副作用并丢弃结果对象是一个好模式吗?我没听错吧?显然你是这个意思。我只是没想到会有这样的答案,因为其他一些有经验的Stackoverflower在前面的问题中称之为黑客攻击。但为什么不呢?毕竟,这创造了一个