Python中静态变量和实例变量的区别。它们真的存在吗?

16 投票
6 回答
13125 浏览
提问于 2025-04-15 15:47

这里有一个随机的类定义:

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 个回答

4

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。

每个人也可以在实例或类中放其他属性,这没错,但那样就没意思了,因为类的代码并不打算使用它们。像上面的例子之所以有趣,是因为类的代码确实在使用这些属性。

27

警告:以下内容是一个过于简单化的解释;我忽略了__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用法。

17
class SomeClass:
  x=6  # class variable

  def __init__(self):
    self.y = 666  # instance variable

声明一个类作用域的变量是有好处的:它可以作为默认值。你可以把类作用域的变量想象成其他语言中的“静态”变量。

撰写回答