具有非None类属性的类的新实例?
我有一个Python类,这个类里面有一个属性,它的初始值不是None
。当我创建一个新的实例时,对这个属性所做的修改会影响到所有的实例。
下面是一些代码,帮助理解这个问题:
class Foo(object):
a = []
b = 2
foo = Foo()
foo.a.append('item')
foo.b = 5
使用foo.a
会返回['item']
,而foo.b
会返回5
,这都是正常的结果。
当我创建一个新的实例(我们叫它bar
)时,使用bar.a
也返回['item']
,bar.b
也返回5
!但是,当我最开始把所有的类属性设置为None
,然后在__init__
方法中把它们设置为其他值,像这样:
class Foo(object):
a = None
b = None
def __init__(self):
self.a = []
self.b = 2
这时使用bar.a
会返回[]
,而bar.b
会返回2
,同时foo.a
依然返回['item']
,foo.b
返回5
。
这是应该这样工作的吗?在我编程三年的时间里,我从来没有遇到过这个问题,希望能得到一些解释。我在文档里也找不到相关信息,如果能给我一个参考就太好了。:)
4 个回答
是的,一开始听起来可能让人感到惊讶,但这就是Python的工作方式,其他回答也提到过。你已经用了三年Python而没有遇到这个问题?你真是太幸运了。如果我是你,我会检查一下以前的代码,看看有没有什么小问题...
另外,调用函数时也要小心:def func(list=[])
这种写法也有类似的行为,可能会让人感到意外。
如果可以的话,建议在你的提交钩子里加上pylint的解析器。或者至少偶尔在你的代码上运行一下pylint,它非常有帮助,可能会告诉你这个问题(其实这不是个问题,逻辑上很合理,只是Python的写法)。
没错,这正是你应该预期的。当你在类里定义一个变量时,这个属性是属于类的,而不是某个具体的实例。你可能不会总是注意到,当你给某个实例的属性赋值时,实例会拿到这个新值,这样就覆盖了类里的属性。像 list.append
这样的修改方法不会给实例添加新的属性,因为它们只是修改了已经存在的对象,而这个对象恰好是类的一个属性。
每当你希望类的每个实例都有自己独特的属性值时,通常应该在 __init__
方法里设置,这样可以确保每个实例的值都是不同的。
只有当一个类的属性有一个合理的默认值,并且这个值是不可变的类型(也就是不能被直接修改的),比如 int
或 str
,你才可以在类里设置属性。
是的,这就是它应该工作的方式。
如果 a
和 b
是 Foo
的一个实例里的内容,那么正确的做法是:
class Foo(object):
def __init__(self):
self.a = []
self.b = 2
下面的代码让 a
和 b
成为 类本身 的一部分,这样所有的实例都会共享同样的变量:
class Foo(object):
a = []
b = 2
当你把这两种方法混在一起使用时——就像你在第二个例子中做的那样——这并没有增加什么有用的东西,反而会让人感到困惑。
值得注意的一点是,当你在第一个例子中这样做时:
foo.b = 5
你并没有改变 Foo.b
,而是给 foo
添加了一个全新的属性,这个属性“遮盖”了 Foo.b
。这样做后,bar.b
和 Foo.b
都不会改变。如果你随后执行 del foo.b
,那么这个属性会被删除,foo.b
将再次指向 Foo.b
。