Python __slots__

摘要:在本教程中,你将学习关于Python中的__slots__以及如何使用它来提高类的效率。

Python __slots__ 的简介


下面定义了一个Point2D类,它有两个属性,即x和y坐标:

class Point2D:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'Point2D({self.x},{self.y})'

每个Point2D类的实例都有自己的__dict__属性,用于存储实例属性。例如:

point = Point2D(0, 0)
print(point.__dict__)

默认情况下,Python使用字典来管理实例属性。字典允许你在运行时动态地向实例添加更多属性。然而,它也会带来一定的内存开销。如果Point2D类有很多对象,就会有很大的内存开销。

为了避免内存开销,Python引入了槽(slots)。如果一个类只包含固定(或预定义)的实例属性,你可以使用槽(slots)来指示Python使用更紧凑的数据结构,而不是字典。

例如,如果Point2D类只有两个实例属性,你可以像这样在槽(slots)中指定属性:

class Point2D:
    __slots__ = ('x', 'y')

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'Point2D({self.x},{self.y})'

在这个例子中,你将一个可迭代对象(元组)赋给了包含类中要使用的属性名称的槽(slots)。

通过这样做,Python将不再使用__dict__来存储类的实例。下面的代码会导致AttributeError错误:

point = Point2D(0, 0)
print(point.__dict__)

错误信息如下:

AttributeError: 'Point2D' object has no attribute '__dict__'

相反,你会在类的实例中看到__slots__。例如:

point = Point2D(0, 0)
print(point.__slots__)

输出结果为:

('x', 'y')

此外,你不能在运行时动态地向实例添加更多属性。以下代码会导致错误:

point.z = 0

错误信息如下:

AttributeError: 'Point2D' object has no attribute 'z'

然而,你可以将类属性添加到类中:

Point2D.color = 'black'
pprint(Point2D.__dict__)

输出结果为:

mappingproxy({'__doc__': None,
              '__init__': <function Point2D.__init__ at 0x000001BBBA841310>,
              '__module__': '__main__',
              '__repr__': <function Point2D

.__repr__ at 0x000001BBBA8413A0>,
              '__slots__': ('x', 'y'),
              'color': 'black',
              'x': <member 'x' of 'Point2D' objects>,
              'y': <member 'y' of 'Point2D' objects>})


这段代码有效的原因是因为 Python 将槽(slots)应用于类的实例,而不是应用于类本身。

Python的__slots__和单继承
让我们在继承的情况下探讨槽(slots)的用法。

基类使用了槽(slots),但子类没有使用
下面的代码定义了一个基类 Point2D 和一个继承自 Point2D 类的子类 Point3D:

class Point2D:
    __slots__ = ('x', 'y')

    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f'Point2D({self.x},{self.y})'


class Point3D(Point2D):
    def __init__(self, x, y, z):
        super().__init__(x, y)
        self.z = z


if __name__ == '__main__':
    point = Point3D(10, 20, 30)
    print(point.__dict__)

输出结果:

{'z': 30}

Point3D 类没有定义槽(slots),因此其实例具有 __dict__ 属性。在这种情况下,子类 Point3D 会使用其基类的槽(slots)(如果可用),并且还会使用实例字典。

如果希望 Point3D 类也使用槽(slots),可以通过以下方式定义额外的属性:

class Point3D(Point2D):
    __slots__ = ('z',)

    def __init__(self, x, y, z):
        super().__init__(x, y)
        self.z = z

请注意,不需要在子类的 __slots__ 中重复指定已经在基类的 __slots__ 中定义的属性。

现在,Point3D 类将使用槽来存储所有属性,包括 x、y 和 z。

基类不使用槽(__slots__),子类使用槽(__slots__)
下面的例子定义了一个基类 Shape,它没有使用槽(__slots__),而子类 Point2D 使用了槽(__slots__):

class Shape:
    pass


class Point2D(Shape):
    __slots__ = ('x', 'y')

    def __init__(self, x, y):
        self.x = x
        self.y = y


if __name__ == '__main__':
    # 使用槽和字典来存储实例属性
    point = Point2D(10, 10)
    print(point.__slots__)
    print(point.__dict__)

    # 可以在运行时添加属性
    point.color = 'black'
    print(point.__dict__)

输出结果:
 

('x', 'y')
{'color': 'black'}

在这种情况下,Point2D 类的实例同时使用了 __slots__ 和字典来存储实例属性。

总结
Python 使用字典来存储类的实例属性。这使得您可以在运行时动态地向实例添加更多属性,但也会增加内存开销。
如果类具有预定义的实例属性,请在类中定义 __slots__,以指示 Python 不要使用字典来存储实例属性。使用 __slots__ 可以优化内存,特别是当类有大量对象时。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值