Python的私有属性和“受保护的”属性
如果以 __mood 的形式(两个前导下划线,尾部没有或最多有一个下划线)命名实例属性, Python 会把属性名存入实例的 __dict__ 属性中,而且会在前面加上一个下划线和类名。这个语言特性叫名称改写(name mangling)。
print(d1.__dict__) # {'_Dog__mood': 'a'}
有些人不喜欢这种句法,他们约定使用一个下划线前缀编写“受保护”的属性(如 self._x)。批评使用两个下划线这种改写机制的人认为,应该使用命名约定来避免意外覆盖属性。
Python 解释器不会对使用单个下划线的属性名做特殊处理,不过这是很多 Python 程序员严格遵守的约定,他们不会在类外部访问这种属性。
使用 __slots__ 类属性节省空间
默认情况下, Python 把实例属性存储在 __dict__ 的字典里。为了使用底层的散列表提升访问速度,字典会消耗大量内存。如果要处理数百万个属性不多的实例,通过 __slots__ 类属性,能节省大量内存,方法是让解释器在元组中存储实例属性,而不用字典。
继承自超类的 __slots__ 属性没有效果。 Python 只会使用各个类中定义的__slots__ 属性。每个子类都要定义 __slots__ 属性,因为解释器会忽略继承的 __slots__ 属性。
定义 __slots__ 的方式是,创建一个类属性,使用 __slots__ 这个名字,并把它的值设为一个字符串构成的可迭代对象,其中各个元素表示各个实例属性:
# AttributeError: 'Vector2d' object has no attribute '__dict__'
在类中定义 __slots__ 属性之后,实例不能再有 __slots__ 中所列名称之外的其他属性。
此外,还有一个实例属性可能需要注意,即 __weakref__ 属性,为了让对象支持弱引用,必须有这个属性。用户定义的类中默认就有 __weakref__ 属性。可是,如果类中定义了 __slots__ 属性,而且想把实例作为弱引用的目标,那么要把'__weakref__' 添加到 __slots__ 中。如果不把 '__weakref__' 加入 __slots__,实例就不能作为弱引用的目标。
类属性覆盖
Python 有个很独特的特性:类属性可用于为实例属性提供默认值。
如果想修改类属性的值,必须直接在类上修改,不能通过实例修改。如果想修改所有实例(没有 typecode 实例变量)的 typecode 属性的默认值,可以这么做:
有种修改方法更符合 Python 风格,而且效果持久,也更有针对性。类属性是公开的,因此会被子类继承,于是经常会创建一个子类,只用于定制类的数据属性。Django基于类的视图就大量使用了这个技术。
把 ShortVector2d 定义为 Vector2d 的子类,只用于覆盖 typecode 类属性。
class ShortVector2d(Vector2d):
这也说明了为什么 Vecto2d.__repr__ 方法中为什么没有硬编码 class_name 的值,而是使用 type(self).__name__ 获取:
cls_name = type(self).__name__