如果只有一个实例,我应该使用实例属性还是类属性?

137 投票
4 回答
110154 浏览
提问于 2025-04-15 22:02

我有一些Python类,在运行时我只需要一个实例,所以每个类的属性只需要有一份,而不是每个实例都有一份。如果有多个实例(但实际上不会发生),那么所有实例应该有相同的配置。我在想下面的选项中,哪个更好或者更符合Python的习惯。

类变量:

class MyController(Controller):

  path = "something/"
  children = [AController, BController]

  def action(self, request):
    pass

实例变量:

class MyController(Controller):

  def __init__(self):
    self.path = "something/"
    self.children = [AController, BController]

  def action(self, request):
    pass

4 个回答

28

如果你不确定,通常你应该使用实例属性。

类属性最好只在特定情况下使用,那些情况下它们才有意义。最常见的用法是方法。使用类属性来存储实例需要知道的只读常量并不罕见(不过这样做的好处主要是你想从类外部访问这些常量),但你一定要小心,不要在类属性中存储任何状态,因为这通常不是你想要的。即使你只会有一个实例,你也应该像写其他类一样来写这个类,这通常意味着使用实例属性。

49

进一步呼应了 MikeAlex 的建议,并加入我自己的看法……

使用实例属性是比较常见的做法,也是更符合 Python 风格的方式。类属性的使用频率相对较低,因为它们的适用场景比较特殊。静态方法和类方法也是如此,它们是为特定情况设计的特殊结构,否则就是一些程序员想炫耀自己懂得 Python 的冷门知识而写的代码。

Alex 在他的回复中提到,访问实例属性会稍微快一点,因为少了一层查找……让我为那些还不太了解这个原理的人进一步解释一下。这和访问变量的方式非常相似,查找的顺序是:

  1. 局部变量
  2. 非局部变量
  3. 全局变量
  4. 内置变量

对于属性访问,顺序是:

  1. 实例属性
  2. 类属性
  3. 基类属性,按照 MRO(方法解析顺序)来确定

这两种技术都是“从内到外”进行查找的,也就是说,最靠近的对象会首先被检查,然后依次检查外层的对象。

在你上面的例子中,假设你要查找 path 属性。当遇到像 "self.path" 这样的引用时,Python 会首先在实例属性中查找匹配项。如果没有找到,它会检查创建这个对象的类。最后,它会搜索基类。正如 Alex 所说,如果你的属性在实例中找到了,就不需要再去其他地方查找,因此节省了一点时间。

不过,如果你坚持使用类属性,就需要额外的查找。或者,你可以通过类来引用对象,而不是通过实例,比如用 MyController.path 而不是 self.path。这样是直接查找,可以避免延迟查找,但正如 Alex 在下面提到的,这样就变成了全局变量,所以你失去了原本想节省的那一点时间(除非你为这个 [全局] 类名创建一个局部引用)。

总的来说,大多数情况下你应该使用实例属性。不过,有时类属性也是合适的选择。如果同时使用这两者,就需要特别小心,因为使用 self 只会得到实例属性对象,而会“遮蔽”同名的类属性。在这种情况下,你 必须 通过类名来访问属性,才能引用到它。

177

如果你只有一个实例的话,最好把所有的变量都设置为每个实例独有的。这是因为这样访问这些变量会稍微快一点(因为从类到实例的“继承”关系少了一层查找),而且没有什么缺点可以和这个小好处相提并论。

撰写回答