在Python中,在__init__中定义成员与在类体中定义成员的区别是什么?
这两段代码有什么区别呢?
class a:
def __init__(self):
self.val=1
和
class a:
val=1
def __init__(self):
pass
3 个回答
正如其他人提到的,有时候属性是在类上,有时候是在实例上。这有什么区别吗?有的,特别是当值是可变的时候。最好的解释就是代码,所以我会加一些代码来展示(这其实就是这个回答的全部内容):
首先定义一个类,里面有两个实例属性。
>>> class A(object):
... def __init__(self):
... self.number = 45
... self.letters = ['a', 'b', 'c']
...
然后定义一个类,里面有两个类属性。
>>> class B(object):
... number = 45
... letters = ['a', 'b', 'c']
...
现在我们来使用它们:
>>> a1 = A()
>>> a2 = A()
>>> a2.number = 15
>>> a2.letters.append('z')
一切都很好:
>>> a1.number
45
>>> a1.letters
['a', 'b', 'c']
现在使用类属性的变体:
>>> b1 = B()
>>> b2 = B()
>>> b2.number = 15
>>> b2.letters.append('z')
结果是...嗯...
>>> b1.number
45
>>> b1.letters
['a', 'b', 'c', 'z']
是的,注意到当你改变了可变的类属性时,它对所有类都发生了变化。这通常不是你想要的结果。
如果你在使用ZODB,你会使用很多类属性,因为这是一个方便的方法,可以用新属性来升级现有对象,或者在类级别添加一些信息,而这些信息不会被保存。否则,你基本上可以忽略它们。
其他人已经解释了技术上的区别。我来试着说明一下为什么你可能想用类变量。
如果你只创建一次这个类的实例,那么类变量实际上就和实例变量一样了。不过,如果你要创建很多个实例,或者想让几个实例之间共享一些状态,类变量就非常有用了。举个例子:
class Foo(object):
def __init__(self):
self.bar = expensivefunction()
myobjs = [Foo() for _ in range(1000000)]
这段代码会让expensivefunction()被调用一百万次。如果每次返回的值都是一样的,比如从数据库中获取一个配置参数,那么你应该考虑把它放到类定义里,这样就只会调用一次,然后在所有实例之间共享这个值。
我在缓存结果的时候也经常使用类变量。比如:
class Foo(object):
bazcache = {}
@classmethod
def baz(cls, key):
try:
result = cls.bazcache[key]
except KeyError:
result = expensivefunction(key)
cls.bazcache[key] = result
return result
在这个例子中,baz是一个类方法;它的结果不依赖于任何实例变量。这意味着我们可以在类变量中保存一份结果缓存,这样1) 你就不会重复存储相同的结果,2) 每个实例都可以利用其他实例缓存的结果。
举个例子,假设你有一百万个实例,每个实例都在处理一次谷歌搜索的结果。你肯定更希望这些对象共享搜索结果,而不是每个实例都去执行搜索并等待答案。
所以我不同意Lennart的看法。在某些情况下,类变量是非常方便的。当它们是解决问题的合适工具时,别犹豫,尽管使用它们。
class a:
def __init__(self):
self.val=1
这段代码创建了一个类(在Python 2中,这是个很糟糕的老式类,不要这样做!;在Python 3中,这种老旧的类终于消失了,所以这就是唯一的好类,创建时需要用 class a(object):
这样的方式)。每个实例一开始都有自己的整数对象 1
的引用。
class a:
val=1
def __init__(self):
pass
这段代码创建了一个同样类型的类,它自己有一个整数对象 1
的引用(它的实例一开始没有每个实例自己的引用)。
对于像 int
这样的不可变对象,实际差别不太明显。例如,无论哪种情况,如果你后来在某个 a
的实例上执行 self.val = 2
,这将创建一个实例引用(之前的回答在这方面是错误的)。
这个区别对可变对象很重要,因为可变对象有修改方法,所以知道某个列表是每个实例独有的还是所有实例共享的非常关键。但对于不可变对象,由于你永远不能改变对象本身,只能重新赋值(比如给 self.val
赋值,这总是会创建每个实例的引用),所以这个区别就不那么重要了。
对于不可变对象,唯一相关的区别是:如果你后来将 a.val = 3
,在第一种情况下,这会影响每个实例看到的 self.val
(除了那些已经给自己的 self.val
赋值的实例);而在第二种情况下,这不会影响任何实例看到的 self.val
(除了那些你执行过 del self.val
或类似操作的实例)。