如果只有一个实例,我应该使用实例属性还是类属性?
我有一些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 个回答
如果你不确定,通常你应该使用实例属性。
类属性最好只在特定情况下使用,那些情况下它们才有意义。最常见的用法是方法。使用类属性来存储实例需要知道的只读常量并不罕见(不过这样做的好处主要是你想从类外部访问这些常量),但你一定要小心,不要在类属性中存储任何状态,因为这通常不是你想要的。即使你只会有一个实例,你也应该像写其他类一样来写这个类,这通常意味着使用实例属性。
进一步呼应了 Mike 和 Alex 的建议,并加入我自己的看法……
使用实例属性是比较常见的做法,也是更符合 Python 风格的方式。类属性的使用频率相对较低,因为它们的适用场景比较特殊。静态方法和类方法也是如此,它们是为特定情况设计的特殊结构,否则就是一些程序员想炫耀自己懂得 Python 的冷门知识而写的代码。
Alex 在他的回复中提到,访问实例属性会稍微快一点,因为少了一层查找……让我为那些还不太了解这个原理的人进一步解释一下。这和访问变量的方式非常相似,查找的顺序是:
- 局部变量
- 非局部变量
- 全局变量
- 内置变量
对于属性访问,顺序是:
- 实例属性
- 类属性
- 基类属性,按照 MRO(方法解析顺序)来确定
这两种技术都是“从内到外”进行查找的,也就是说,最靠近的对象会首先被检查,然后依次检查外层的对象。
在你上面的例子中,假设你要查找 path
属性。当遇到像 "self.path
" 这样的引用时,Python 会首先在实例属性中查找匹配项。如果没有找到,它会检查创建这个对象的类。最后,它会搜索基类。正如 Alex 所说,如果你的属性在实例中找到了,就不需要再去其他地方查找,因此节省了一点时间。
不过,如果你坚持使用类属性,就需要额外的查找。或者,你可以通过类来引用对象,而不是通过实例,比如用 MyController.path
而不是 self.path
。这样是直接查找,可以避免延迟查找,但正如 Alex 在下面提到的,这样就变成了全局变量,所以你失去了原本想节省的那一点时间(除非你为这个 [全局] 类名创建一个局部引用)。
总的来说,大多数情况下你应该使用实例属性。不过,有时类属性也是合适的选择。如果同时使用这两者,就需要特别小心,因为使用 self
只会得到实例属性对象,而会“遮蔽”同名的类属性。在这种情况下,你 必须 通过类名来访问属性,才能引用到它。
如果你只有一个实例的话,最好把所有的变量都设置为每个实例独有的。这是因为这样访问这些变量会稍微快一点(因为从类到实例的“继承”关系少了一层查找),而且没有什么缺点可以和这个小好处相提并论。