与可变对象的默认参数相关的意外python行为

时间:2013-11-06 10:27:15

标签: python

#! /usr/bin/python

class my_class:
    # 1. __init__
    def __init__(self):
        self.my_set = set()

    # 2. __init__
    #def __init__(self, arg_set = set()):
    #    self.my_set = arg_set 

c1 = my_class()
c1.my_set.add('a')

print c1.my_set

c2 = my_class()
c2.my_set.add('b')

print c1.my_set

my_class有两种定义__init__的方式:

如果我使用第一种方式,输出是预期的:
    set(['a'])
    set(['a'])

如果我使用第二种方式,输出是意外的:
    set(['a'])
    set(['a', 'b'])

第二种方式出了什么问题?如何修改C2(一个单独的对象),导致修改c1?

Edit: Updated the question title to reflect specific area of concern

2 个答案:

答案 0 :(得分:5)

来自http://docs.python.org/2/reference/compound_stmts.html#function-definitions

  

当函数定义为时,将计算默认参数值   执行。这意味着表达式被评估一次,当时   定义函数,并使用相同的“预先计算”值   每次通话。这对了解何时来说尤为重要   default参数是一个可变对象,例如列表或字典:   如果函数修改了对象(例如通过将项附加到a   list),默认值实际上已被修改。

这就是你的第二个方法每次都附加值的原因。

此外,像这样修改第二个__init__

def __init__(self, arg_set = set()):
   print id(arg_set)
   self.my_set = arg_set 

现在,当您运行代码时,您将始终获得相同的地址(CPython中的id函数返回内存中对象的地址)。 因此,每次调用函数时都不会创建默认参数,但是第一次评估时会创建默认参数。

答案 1 :(得分:1)

thefourtheye对这种行为的原因是完全正确的。如果你想使用构造函数的第二个版本,它应该是这样的:

def __init__(self, arg_set=None):
    if arg_set is None:
        self.my_set = set()
    else:
        self.my_set = arg_set