类变量、成员变量,与注解
摘要
Python 对定义在类体内的变量属于类变量还是成员变量(下称实例变量)的区分并不十分明确,这里记录一下我了解到的最佳实践。
以
self.x
形式定义的就是成员变量,没有歧义;定义在类内的变量,即是类变量,也是成员变量;因为两个同名的变量会分别被定义在了类和实例的
__dict__
中;它们之间除了同名没有其他关系;实例变量在被修改前并不会被真的定义出来,而是访问同名的类变量(相当于默认值);一旦被修改,就会立即在实例中创建一个同名的实例变量,将两者隔离开来;
因此正确的做法是,通过实例对象访问那就是实例变量,通过类对象访问的就是类变量;
class A: x: int = 1 a = A() print(A.x) # 1 # 类变量 print(a.x) # 1 # 实例变量(此时并没有被真的创建,而是调用类变量作为默认值) print('x' in a.__dict__) # False # 实例变量并没有被真的定义出来 a.x += 1 # 实例变量被修改 print(A.x) # 1 # 类变量 print(a.x) # 2 # 实例变量 print('x' in a.__dict__) # True # 实例变量一旦被修改,就会立即被创建出来
基本使用
下面是一段来自官方的代码:
class BasicStarship: captain: str = 'Picard' # instance variable with default damage: int # instance variable without default stats: ClassVar[Dict[str, int]] = {} # class variable
captain
应该是“类变量”(因为保存在BasicStarship.__dict__
中),但官方的注释却是“实例变量”(成员变量)。我理解其中的逻辑可能是这样的:
记
bs = BasicStarship()
;以
bs.captain
的方式访问captain
,会按照bs.__dict__ -> BasicStarship.__dict__
的路径依次查找;而任何通过
bs.captain
对captain
的修改,都会在bs
中添加一个新的成员变量,同时不会影响BasicStarship.captain
bs = BasicStarship() print(bs.captain) # Picard print(BasicStarship.captain) # Picard print('captain' in bs.__dict__) # False bs.captain += '_local' # setattr(bs, 'captain', BasicStarship.captain + '_local') print(bs.captain) # Picard_local print(BasicStarship.captain) # Picard print('captain' in bs.__dict__) # True
所以我认为定义在类体内的变量(已初始化),即是类变量,也是实例变量;因为它被分别定义在了类和实例的
__dict__
中(虽然实例变量没有被立即定义),它们之间除了同名之外没有其他关系;因此正确的做法应该是,通过实例对象访问那就是实例变量(
bs.captain
),通过类对象访问的就是类变量(BasicStarship.captain
);
最佳实践
TODO
参考
Last updated