我有一个Python类,其class属性设置为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
}。
这是假设的工作原理吗?在我编写Python的3年里,我显然从未遇到过这个问题,并希望得到一些澄清。我也无法在文档中的任何地方找到它,所以如果可能的话,给我一个很好的参考。 :)
答案 0 :(得分:7)
是的,这就是应该如何运作的。
如果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
。
答案 1 :(得分:1)
是的,这正是你应该期待的。在类上定义变量时,这些属性将附加到类,而不是实例。您可能并不总是注意到,当将分配给实例上的属性时,实例将获取新值,从而屏蔽class属性。 就地修改的方法(如list.append
)不会为实例提供新属性,因为它们只是修改现有对象,这恰好是类的属性。
每当一个类的每个实例都应该有一个属性的唯一值时,通常应该在__init__
方法中设置它,以确保它对每个实例都不同。
只有当一个类的属性具有合理的默认值,并且该值属于不可变类型(无法就地修改)时,如int
或str
,在类上设置属性。
答案 2 :(得分:1)
是的,在开始时似乎令人惊讶,但正如其他答案中所述,这是python的工作方式。三年的蟒蛇没有被这个削减?你很幸运,非常幸运。如果我是你,我会检查敏感的过去代码中的小动物......
对函数调用也要小心:def func(list=[])
有类似的行为,这可能会令人惊讶。
并且,如果可能的话,在提交挂钩中插入一个pylint解析。或者至少在你的代码上运行pylint,它非常有启发性,并且可能已经告诉过你关于这个技巧(这不是技巧,事实上,它是非常逻辑和正交的,只是python方式)。
答案 3 :(得分:0)
您将class属性与实例属性混淆。在第一个示例中,只有类属性a
和b
,但在第二个示例中,您还有具有相同名称的实例属性。
如果没有实例属性(我今天学到了新东西!),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