2013-10-20 45 views
1
class Foo: 
    def __init__(self, stuff = []): 
     print(stuff) 
     self.stuff = stuff 
    def add(self,x): 
     self.stuff.append(x) 


>>>f = Foo() 
[] 
>>>f.add(1) 
>>>g = Foo() 
[1] 

我只作一个改变的代码(4号线)可变默认值

class Foo: 
    def __init__(self, stuff = []): 
     print(stuff) 
     self.stuff = stuff or [] 
    def add(self,x): 
     self.stuff.append(x) 


>>>f = Foo() 
[] 
>>>f.add(1) 
>>>g = Foo() 
[] 

我改变的东西在第4行,但导致的打印结果的变化(这是在行3)

我只是想知道它是如何工作的。

回答

1

在这一行:

def __init__(self, stuff = []): 

Foo()创建将使用相同的列表,作为默认值创造了__init__所有调用所有实例。这就是为什么在第一个示例中添加任何内容f导致修改默认列表全部Foo的进一步实例。这是因为当解释器评估def行时,评估部分stuff = []。从documentation

默认执行函数定义当参数值被评估。这意味着该表达式在函数被定义时被评估一次,并且每次调用都使用相同的“预先计算”值。

(粗体由我添加)。

在第二示例中,作为默认的始终是空的or子句的第二部分被评估它创建的列表的一个新的,单独的实例中的每个被创建的Foo一个新实例的时间。因此,所有Foo实例都将拥有自己的列表。

Here你可以找到更多的解释和例子。

1

传递作为后备参数值列表被实例化一次,整个每次调用重用,以__init__。因此,向其中添加一个值将传播到Foo的每个下一个实例。

第一个和第二个示例之间的区别是stuff or []。您需要知道空的列表(如果评估为布尔值时为False),则stuff or []表达式将返回第二个操作数,如果stuff为空,则在您的示例中将始终如此。第二个操作数是在方法调用中实例化的列表,因此每次调用一个不同的实例__init__。这确保了附加值不会传播。