为什么通过Python默认变量初始化的变量在对象实例化之间保持状态?

12 投票
3 回答
10269 浏览
提问于 2025-04-16 05:11

今天我遇到了一个有趣的Python问题,就是反复创建一个类的实例时,似乎会保持某种状态。在后续的实例化调用中,变量已经被定义了。

我把这个问题简化成了以下的类和命令行的互动。我知道这不是初始化类变量的最佳方式,但它的表现确实不应该是这样的。这算不算一个真正的bug,还是说这是个“特性”?:D

tester.py:

class Tester():
        def __init__(self):
                self.mydict = self.test()

        def test(self,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 2.6.6 (r266:84292, Oct  6 2010, 00:44:09) 
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
>>> import tester
>>> t = tester.Tester()
>>> print t.mydict
{'key': 'a,b,c,d'}
>>> t2 = tester.Tester()
>>> print t2.mydict
{'key': 'a,b,c,d,a,b,c,d'}

3 个回答

3

你正在修改方法中函数参数 out 的值。

这篇博客文章 简单明了地解释了这个问题:

默认参数中的表达式是在函数定义的时候计算的,而不是在函数被调用的时候。

函数是在类创建的时候定义的,而不是为每个实例单独定义的。如果你这样修改,就能解决这个问题:

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
5

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

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

想了解更多为什么这样做是必要的,以及为什么这被认为是Python语言的一个“特性”而不是一个错误,可以查看这些链接。

15

这是一个几乎所有Python用户都会遇到的功能,通常会碰到一两次。它的主要用途是用来缓存数据,避免重复进行耗时的计算(其实就是简单的记忆化),不过我相信大家也会找到其他的用法。

原因在于,def语句只会执行一次,也就是在定义函数的时候。因此,初始化的值只会创建一次。对于像列表或字典这样的引用类型(与不可变类型不同,不可变类型是不能改变的),这就会变成一个明显且令人惊讶的陷阱,而对于值类型来说,这种情况通常不会被注意到。

通常,人们会这样来解决这个问题:

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

撰写回答