为啥通过 python 默认变量初始化变量会在对象实例化过程中保持状态?

Posted

技术标签:

【中文标题】为啥通过 python 默认变量初始化变量会在对象实例化过程中保持状态?【英文标题】:Why does initializing a variable via a python default variable keep state across object instantiation?为什么通过 python 默认变量初始化变量会在对象实例化过程中保持状态? 【发布时间】:2011-04-22 16:08:31 【问题描述】:

我今天遇到了一个有趣的 python 错误,其中反复实例化一个类似乎处于保持状态。在稍后的实例化调用中,该变量已经定义。

我将问题归结为以下类/shell 交互。我意识到这不是初始化类变量的最佳方法,但它肯定不应该这样。这是一个真正的错误还是这是一个“功能”? :D

tester.py:

类测试器(): def __init__(self): self.mydict = self.test() 定义测试(自我,out = ): 键=“键” 对于 ['a','b','c','d'] 中的 i: 如果输入输出: 出[键] += ','+i 别的: 出[键] = 我 返回

Python 提示:

Python 2.6.6(r266:84292,2010 年 10 月 6 日,00:44:09) [GCC 4.2.1 (Apple Inc. build 5664)] 在达尔文 >>> 导入测试仪 >>> t = tester.Tester() >>> 打印 t.mydict 'key': 'a,b,c,d' >>> t2 = tester.Tester() >>> 打印 t2.mydict 'key': 'a,b,c,d,a,b,c,d'

【问题讨论】:

它会不会将out 解释为一个全局变量,从而在您调用test 时不断添加它? 复制:***.com/questions/1132941/… "Least Astonishment" and the Mutable Default Argument的可能重复 【参考方案1】:

这是一个几乎所有 Python 用户都会遇到一次或两次的功能。主要用于缓存等以避免重复冗长的计算(实际上是简单的记忆),尽管我相信人们已经找到了它的其他用途。

原因是def 语句只执行一次,也就是定义函数的时候。因此初始化器值只被创建一次。对于像列表或字典这样的引用类型(相对于不能更改的不可变类型),这最终会成为一个可见且令人惊讶的陷阱,而对于值类型,它会被忽视。

通常,人们会这样处理它:

def test(a=None):
    if a is None:
        a = 
    # ... etc.

【讨论】:

你很好的解释。我想添加另一行。 function 是 python 中的一个对象,当我们第一次调用 function 对象时,会创建一个对象,之后我们会重用同一个对象。您可以通过在每次通话后打印 id(Test) 来确认。您将获得相同的函数对象 ID,这意味着只有一个对象在共享。【参考方案2】:

一般来说,默认方法参数不应该是可变的。而是这样做:

def test(self, out=None):
   out = out or 
   # other code goes here.

请参阅这些链接,了解有关为什么这是必要的以及为什么它是 python 语言的“功能”而不是错误的更多详细信息。

"Least Astonishment" and the Mutable Default Argument http://effbot.org/zone/default-values.htm

【讨论】:

out = out or 不仅有一种腐朽的味道,而且如果调用者传入他们自己的空映射对象,也会失败。 if out is None: out = 更可取。【参考方案3】:

您正在修改方法中函数关键字参数out 的值。

This blog post 简洁解释:

默认参数中的表达式在定义函数时计算在调用时计算。

函数是在创建类时定义的,而不是针对每个实例。如果你像这样修改它,问题就会消失:

def test(self,out=None):
        if out is None:
                out = 
        key = "key"
        for i in ['a','b','c','d']:
                if key in out:
                        out[key] += ','+i
                else:   
                        out[key] = i 
        return out

【讨论】:

以上是关于为啥通过 python 默认变量初始化变量会在对象实例化过程中保持状态?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Python 类初始化变量不能用于包导入中的类实例对象,但否则?

JVM学习--局部变量表

JS全局变量是全局对象的属性,函数局部变量为啥就不是函数的属性呢?

C++中的复制构造函数

为啥 Scala 语言要求您初始化实例变量而不是依赖默认值?

为什么全局变量无须初始化 系统自动给默认值,局部变量必须初始化?