Python中静态变量和实例变量的区别。它们真的存在吗?
这里有一个随机的类定义:
class ABC:
x = 6
接下来,我们为abc这个实例设置一些值,然后为静态变量设置值:
abc = ABC()
abc.x = 2
ABC.x = 5
然后打印结果:
print abc.x
print ABC.x
打印出来的是:
2
5
现在,我有点搞不清楚状况,因为如果我把类定义中的x = 6换成"pass",输出的结果还是一样。我的问题是,在Python中定义一个变量在类里有什么意义呢?因为我好像随时都可以设置任何变量,而不需要这样做。
另外,Python能区分实例变量和静态变量吗?从我看到的情况来看,我觉得是可以的。
6 个回答
Python在这方面是有区别的。目的可能有很多,但举个例子来说:
class token(object):
id = 0
def __init__(self, value):
self.value = value
self.id = token.id
token.id += 1
在这里,类变量token.id
会在每创建一个新实例时自动增加,而这个实例可以同时拥有一个独特的ID,这个ID会被放在self.id
里。它们存储的位置不同——一个在类对象里,另一个在实例对象里。你可以把它们想象成一些面向对象编程语言(比如C++或C#)中的静态变量和实例变量。
在这个例子中,如果你执行:
print token.id
你会看到下一个要分配的ID,而:
x = token(10)
print x.id
则会给出那个实例的ID。
每个人也可以在实例或类中放其他属性,这没错,但那样就没意思了,因为类的代码并不打算使用它们。像上面的例子之所以有趣,是因为类的代码确实在使用这些属性。
警告:以下内容是一个过于简单化的解释;我忽略了__new__()
和其他一些特殊的类方法,也省略了很多细节。但这个解释能让你在Python中走得很远。
当你在Python中创建一个类的实例,比如在你的例子中调用ABC()时:
abc = ABC()
Python会创建一个新的空对象,并将它的类设置为ABC。然后,如果有__init__()
方法,它会被调用。最后,Python会返回这个对象。
当你请求一个对象的属性时,首先会在这个实例中查找。如果找不到,就会去它的类中查找。接着再查找基类,依此类推。如果最终还是找不到这个属性,Python就会抛出一个异常。
当你给对象的一个属性赋值时,如果这个对象还没有这个属性,就会创建一个新的属性。然后将这个属性设置为你赋的值。如果这个对象已经有了同名的属性,Python会丢掉对旧值的引用,改为指向新值。
这些规则让你观察到的行为变得容易预测。在这行代码之后:
abc = ABC()
只有ABC对象(类)有一个名为x的属性。abc实例还没有自己的x,所以如果你请求这个属性,你会得到ABC.x的值。但随后你在类和对象上都重新赋值了属性x。当你检查这些属性时,你会发现你放进去的值依然存在。
现在你应该能预测这段代码的作用:
class ABC:
x = 6
a = ABC()
ABC.xyz = 5
print(ABC.xyz, a.xyz)
没错:它会打印两个五。你可能会期待它抛出一个AttributeError异常。但Python在类中找到了这个属性——即使它是在实例创建之后添加的。
这种行为可能会让你陷入麻烦。一个经典的初学者错误是:
class ABC:
x = []
a = ABC()
a.x.append(1)
b = ABC()
print(b.x)
这会打印[1]。所有的ABC()实例共享同一个列表。你可能想要的是这个:
class ABC:
def __init__(self):
self.x = []
a = ABC()
a.x.append(1)
b = ABC()
print(b.x)
这会打印一个空列表,正如你所期待的。
为了回答你的具体问题:
我的问题是,如果我可以随时设置任何变量,定义一个变量在类定义中的目的是什么?
我猜这意味着“我为什么要在类内部赋值,而不是在__init__
方法中?”
从实际角度来看,这意味着实例没有自己的属性副本(或者至少还没有)。这使得实例更小;同时也意味着访问这个属性会更慢。并且这意味着所有实例共享同一个属性值,这在可变对象的情况下可能不是你想要的。最后,在这里赋值意味着这个值是类的属性,这也是设置类属性的最直接方式。
从风格上讲,这样的代码更简洁,因为你不需要到处写self.。除此之外,差别不大。不过,在__init__
方法中赋值可以确保它们是明确的实例变量。
我自己并不是特别一致。我唯一确定的做法是把所有我不想共享的可变对象在__init__
方法中赋值。
另外,Python能区分实例变量和静态变量吗?从我看到的情况来看,我认为可以。
Python类没有像C++那样的类静态变量。只有属性:类对象的属性和实例对象的属性。如果你请求一个属性,而实例没有这个属性,你会得到类中的属性。
在Python中,最接近类静态变量的概念是一个隐藏的模块属性,像这样:
_x = 3
class ABC:
def method(self):
global _x
# ...
这并不是类的一部分。但这是一个常见的Python用法。
class SomeClass:
x=6 # class variable
def __init__(self):
self.y = 666 # instance variable
声明一个类作用域的变量是有好处的:它可以作为默认值。你可以把类作用域的变量想象成其他语言中的“静态”变量。