2016-07-13 22 views
0

如果我在Python中创建一个类,我给它一个类属性(这是直接取自文档,here),作为从实例修改类属性,根据类型不同的行为?

class Dog: 

    tricks = []    

    def __init__(self, name): 
     self.name = name 

    def add_trick(self, trick): 
     self.tricks.append(trick) 

我看到的是,作为文档建议,在做的时候

d1 = Dog('d1') 
d2 = Dog('d2') 

d1.add_trick('trick') 
print d2.tricks 

我得到的伎俩被添加到D2,以及:

['trick'] 

这是因为tricks是一个类属性,而不是一个插件tance属性在所有实例中共享(如果这不是正统的话,请纠正我)。

现在,假设我这样做,而不是

class Dog: 

    a = 1 

    def __init__(self, name): 
     self.name = name 

    def improve_a(self): 
     self.a += 1 

,我跑

d1 = Dog('d1') 
d2 = Dog('d2') 

d1.improve_a() 
print d1.a, d2.a 

这分别给了我2和1,即二审计数并没有改变。为什么这是行为差异?

+0

'self.a + = 1'确实执行为'self.a = self.a + 1' ... – deceze

+3

因为在一种情况下,您正在变更所有实例共享的可变类属性(http:///stackoverflow.com/q/1680528/3001761),另一方面,你使用不可变的实例属性来映射不可变的类属性。他们的情况并非如此。 *“取决于类型的不同行为”*正是您应该期望的,否则OOP中的事情确实很无聊! – jonrsharpe

回答

2

int类没有定义+ =运算符(__iadd__方法)。这是没有道理的,因为它是不可改变的。

这就是为什么+=默认为+,然后=reference

self.a += 1成为self.a = self.a + 1

现在你第一次调用improve_a会发生以下情况:

  1. 读取的类属性a,并把它在栈上
  2. 添加1到该项目在栈上
  3. 创建新实例属性a并为其分配栈

这意味着class属性的值根本改变,添加新的实例属性。

对于同一对象上的每个后续调用improve,实例属性都会增加,因为属性查找是在实例dict上开始的,并且只在该属性不存在的情况下才会转到类dict。

如果你做同样的与重载__iadd__方法可以得到不同的行为可变类:

class HasList: 
    some_list = [] 

    def add_something(self, value): 
     some_list += [value] 

fst = HasList() 
sec = HasList() 

fst.add_something(1) 
fst.add_something(2) 
sec.add_something(3) 

print(HasList.some_list, fst.some_list, sec.some_list) 

你会看到,所有实例和类本身仍持有相同的列表。打印每次显示相同的列表[1, 2, 3]。您还可以检查所有三个列表是否相同:fst.some_list is sec.some_list and fst.some_list is HasList.some_list # -> True

这是因为list.__iadd__只是调用list.extend并在方法内返回自己(至少如果它是用python写的)。

相关问题