4

我有一个 Python 类,它的类属性设置为None. 创建新实例时,对该属性所做的更改会在所有实例中持续存在。

这里有一些代码可以理解这一点:

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

 foo = Foo()
 foo.a.append('item')
 foo.b = 5

正如人们所期望的那样,使用foo.areturns['item']foo.breturns 。5

当我创建一个新实例(我们将调用它bar)时,也使用bar.areturn['item']bar.breturn 5!但是,当我最初将所有类属性设置为None然后将它们设置为 in 时__init__,如下所示:

 class Foo(object):
   a = None
   b = None

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

在返回和返回时使用bar.a返回和返回。[]bar.b2foo.a['item']foo.b5

这是它应该如何工作的?在我编写 Python 的 3 年里,我显然从未遇到过这个问题,希望得到一些澄清。我在文档中的任何地方也找不到它,所以如果可能的话,给我一个参考会很棒。:)

4

4 回答 4

8

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

如果ab属于 的实例Foo那么正确的做法是:

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

以下构成ab属于类本身,因此所有实例共享相同的变量:

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

当你混合这两种方法时——就像你在第二个例子中所做的那样——这不会增加任何有用的东西,只会引起混乱。

值得一提的是,当您在第一个示例中执行以下操作时:

foo.b = 5

你没有改变,你正在为那个“阴影”Foo.b添加一个全新的属性。当你这样做时,既不会也不会改变。如果您随后这样做,那将删除该属性并再次引用.fooFoo.bbar.bFoo.bdel foo.bfoo.bFoo.b

于 2011-10-26T14:10:41.357 回答
1

是的,这正是您应该期待的。当您在类上定义变量时,这些属性将附加到类,而不是实例。您可能并不总是注意到,当您分配给实例的属性时,实例会获取新值,从而屏蔽类属性。就地修改的方法不会list.append给实例一个新的属性,因为它们只是修改现有的对象,而这恰好是类的一个属性。

每当一个类的每个实例都应该有自己的、唯一的属性值时,您通常应该在__init__方法中设置它,以确保每个实例都不同。

只有当一个类的属性具有合理的默认值,并且该值是不可变类型(不能就地修改)时,例如intor str,您才应该在该类上设置属性。

于 2011-10-26T14:14:53.650 回答
1

是的,一开始似乎令人惊讶,但正如其他答案所述,这是 python 的工作方式。三年蟒蛇不被此斩首?你很幸运,非常幸运。如果我是你,我会检查小动物的敏感过去代码......

还要小心函数调用:def func(list=[])有类似的行为,这可能会令人惊讶。

并且,如果可能的话,在你的提交钩子中插入一个 pylint 解析。或者至少在你的代码上运行 pylint ,它非常有启发性,并且可能会告诉你这个技巧(这不是一个技巧,事实上,它是非常逻辑和正交的,只是 python 方式)。

于 2011-10-26T16:31:23.603 回答
0

您将类属性与实例属性混淆了。在您的第一个示例中,只有类属性ab,但在第二个示例中,您也有同名的实例属性。

如果没有实例属性,Python 似乎会将对属性的引用从一个实例传递给一个类(我今天学到了一些新东西!)。

您可能会发现此代码很有启发性:

class Foo:
  a = 1
  b = 2

  def __init__(self):
    self.a=3

print Foo.a   # => 1
print Foo().a # => 3
print Foo.b   # => 2
print Foo().b # => 2
于 2011-10-26T14:14:22.550 回答