实例属性与类属性访问的区别
我有一个Python类
class pytest:
i = 34
def func(self):
return "hello world"
当我访问 pytest.i
时,我得到了34。我还可以用另一种方式做到这一点:
a = pytest()
a.i
这样也会得到34。
如果我尝试访问一个不存在的 pytest.j
,我会得到
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
pytest.j
AttributeError: class pytest has no attribute 'j'
而当我尝试 a.j
时,错误是
Traceback (most recent call last):
File "<pyshell#8>", line 1, in <module>
a.j
AttributeError: pytest instance has no attribute 'j'
所以我的问题是:这两种情况到底发生了什么,区别是什么呢?
2 个回答
简单来说,跟类和对象相关的变量有两种:类变量和实例变量。类变量是和类相关的,而实例变量是和对象相关的。下面是个例子:
class TestClass:
classVar = 0
def __init__(self):
self.instanceVar = 0
classVar是一个类变量,它和TestClass这个类有关。instanceVar是一个实例变量,它和TestClass类型的对象有关。
print(TestClass.classVar) # prints 0
instance1 = TestClass() # creates new instance of TestClass
instance2 = TestClass() # creates another new instance of TestClass
instance1和instance2共享classVar,因为它们都是TestClass类型的对象。
print(instance1.classVar) # prints 0
TestClass.classVar = 1
print(instance1.classVar) # prints 1
print(instance2.classVar) # prints 1
但是,它们各自都有自己的instanceVar的副本,因为实例变量是和每个具体的实例相关的,而不是和类相关的。
print(instance1.instanceVar) # prints 0
print(TestClass.instanceVar) # error! instanceVar is not a class variable
instance1.instanceVar = 1
print(instance1.instanceVar) # prints 1
print(instance2.instanceVar) # prints 0
正如Aaron所说,如果你想访问一个实例变量,Python会先检查这个对象的实例变量,然后再检查这个对象类型的类变量。类变量可以看作是实例变量的默认值。
不,这两个是不同的概念。
在Python中,所有东西都是对象。类是对象,函数也是对象,实例也是对象。因为一切都是对象,所以它们的表现方式很相似。在你的例子中,你创建了一个名为“pytest”的类实例(也就是一个类型为“Class”的对象)。这个对象有两个属性:i
和fuc
。i
是“整数”或“数字”的实例,而fuc
是“函数”的实例。
当你使用“pytest.j”时,你是在告诉Python:“去查找对象pytest
,然后在找到后再查找i
。”虽然“pytest”是一个类实例,但这并不重要。
当你创建一个“pytest”的实例(也就是一个类型为“pytest”的对象)时,你就得到了一个有“默认值”的对象。在你的例子中,a
是pytest
的一个实例,这意味着任何在a
中找不到的东西,接下来都会在pytest
中查找。
所以a.j
的意思是:“先在a
中查找。如果找不到,再去pytest
中查找。”但是j
并不存在,Python现在需要给你一个有意义的错误信息。它可能会说“类pytest没有属性'j'”。这虽然是正确的,但没有什么实际意义:你得自己搞清楚是通过a
去访问j
的,这会让人困惑。Guido可不想让你这样。
因此,Python使用了不同的错误信息。因为它并不总是能知道实例的名字(a
),设计者决定用类型来代替,所以你会看到“pytest实例...”这样的提示。