具有非None类属性的类的新实例?

4 投票
4 回答
2212 浏览
提问于 2025-04-17 05:02

我有一个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 个回答

1

是的,一开始听起来可能让人感到惊讶,但这就是Python的工作方式,其他回答也提到过。你已经用了三年Python而没有遇到这个问题?你真是太幸运了。如果我是你,我会检查一下以前的代码,看看有没有什么小问题...

另外,调用函数时也要小心:def func(list=[]) 这种写法也有类似的行为,可能会让人感到意外。

如果可以的话,建议在你的提交钩子里加上pylint的解析器。或者至少偶尔在你的代码上运行一下pylint,它非常有帮助,可能会告诉你这个问题(其实这不是个问题,逻辑上很合理,只是Python的写法)。

1

没错,这正是你应该预期的。当你在类里定义一个变量时,这个属性是属于类的,而不是某个具体的实例。你可能不会总是注意到,当你给某个实例的属性赋值时,实例会拿到这个新值,这样就覆盖了类里的属性。像 list.append 这样的修改方法不会给实例添加新的属性,因为它们只是修改了已经存在的对象,而这个对象恰好是类的一个属性。

每当你希望类的每个实例都有自己独特的属性值时,通常应该在 __init__ 方法里设置,这样可以确保每个实例的值都是不同的。

只有当一个类的属性有一个合理的默认值,并且这个值是不可变的类型(也就是不能被直接修改的),比如 intstr,你才可以在类里设置属性。

8

是的,这就是它应该工作的方式。

如果 abFoo 的一个实例里的内容,那么正确的做法是:

class Foo(object):
   def __init__(self):
     self.a = []
     self.b = 2

下面的代码让 ab 成为 类本身 的一部分,这样所有的实例都会共享同样的变量:

class Foo(object):
   a = []
   b = 2

当你把这两种方法混在一起使用时——就像你在第二个例子中做的那样——这并没有增加什么有用的东西,反而会让人感到困惑。

值得注意的一点是,当你在第一个例子中这样做时:

foo.b = 5

你并没有改变 Foo.b,而是给 foo 添加了一个全新的属性,这个属性“遮盖”了 Foo.b。这样做后,bar.bFoo.b 都不会改变。如果你随后执行 del foo.b,那么这个属性会被删除,foo.b 将再次指向 Foo.b

撰写回答