名称改写(name mangling) 、__slots__、 类属性覆盖

本文探讨Python中的私有属性实现机制,包括名称改写和受保护属性的概念。同时,深入解析如何利用__slots__类属性优化内存使用,节省空间,以及其对弱引用的支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python的私有属性和“受保护的”属性

如果以 __mood 的形式(两个前导下划线,尾部没有或最多有一个下划线)命名实例属性, Python 会把属性名存入实例的 __dict__ 属性中,而且会在前面加上一个下划线和类名。这个语言特性叫名称改写(name mangling)。

class Dog:

    def __init__(self, mood):

        self.__mood = mood

d1 = Dog('a')

print(d1.__dict__# {'_Dog__mood': 'a'}

有些人不喜欢这种句法,他们约定使用一个下划线前缀编写“受保护”的属性(如 self._x)。批评使用两个下划线这种改写机制的人认为,应该使用命名约定来避免意外覆盖属性。

Python 解释器不会对使用单个下划线的属性名做特殊处理,不过这是很多 Python 程序员严格遵守的约定,他们不会在类外部访问这种属性。

 

使用 __slots__ 类属性节省空间

默认情况下, Python 把实例属性存储在 __dict__ 的字典里。为了使用底层的散列表提升访问速度,字典会消耗大量内存。如果要处理数百万个属性不多的实例,通过 __slots__ 类属性,能节省大量内存,方法是让解释器在元组中存储实例属性,而不用字典。

继承自超类的 __slots__ 属性没有效果。 Python 只会使用各个类中定义的__slots__ 属性。每个子类都要定义 __slots__ 属性,因为解释器会忽略继承的 __slots__ 属性。

定义 __slots__ 的方式是,创建一个类属性,使用 __slots__ 这个名字,并把它的值设为一个字符串构成的可迭代对象,其中各个元素表示各个实例属性:

class Vector2d:

    __slots__ = ('__x', '__y')

 

v1 = Vector2d(3, 4)

# AttributeError: 'Vector2d' object has no attribute '__dict__'

print(v1.__dict__)

在类中定义 __slots__ 属性之后,实例不能再有 __slots__ 中所列名称之外的其他属性。

此外,还有一个实例属性可能需要注意,即 __weakref__ 属性,为了让对象支持弱引用,必须有这个属性。用户定义的类中默认就有 __weakref__ 属性。可是,如果类中定义了 __slots__ 属性,而且想把实例作为弱引用的目标,那么要把'__weakref__' 添加到 __slots__ 中。如果不把 '__weakref__' 加入 __slots__,实例就不能作为弱引用的目标。

类属性覆盖

Python 有个很独特的特性:类属性可用于为实例属性提供默认值。

如果想修改类属性的值,必须直接在类上修改,不能通过实例修改。如果想修改所有实例(没有 typecode 实例变量)的 typecode 属性的默认值,可以这么做:

Vector2d.typecode = 'f'

有种修改方法更符合 Python 风格,而且效果持久,也更有针对性。类属性是公开的,因此会被子类继承,于是经常会创建一个子类,只用于定制类的数据属性。Django基于类的视图就大量使用了这个技术。

把 ShortVector2d 定义为 Vector2d 的子类,只用于覆盖 typecode 类属性。

class ShortVector2d(Vector2d):

    typecode = 'f'

这也说明了为什么 Vecto2d.__repr__ 方法中为什么没有硬编码 class_name 的值,而是使用 type(self).__name__ 获取:

def __repr__(self):

    cls_name = type(self).__name__

    # 因为Vector2d是一个可迭代对象,所以可以用*self解包

    return '{0}({1}, {2})'.format(cls_name, *self)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值