为了账号安全,请及时绑定邮箱和手机立即绑定

Python - __slots__属性详解

标签:
Python

简介

__slots__允许我们声明并限定类成员,并拒绝类创建__dict____weakref__属性以节约内存空间

Python是动态语言,对于普通的类,可以为类实例赋值任何属性,这些属性会存储在__dict__中:

>>> class Student(object):...     pass...     >>> Abey = Student()>>> Abey.name = 'Abey'>>> Abey.__dict__
{'name': 'Abey'}

这样的特性带来两个问题:

  • 数据通过字典(Hash)存储所占用的空间较大

  • 如何禁止随意生成类属性

当然,__slots__就能解决这两个问题。通过__slots__属性限定类属性的创建:

>>> class Student(object):...     __slots__ = ('name', 'age')
...     
>>> Abey = Student()>>> Abey.name = 'Abey'>>> Abey.gender = 'Female'Traceback (most recent call last):
  File "<input>", line 1, in <module>AttributeError: 'Student' object has no attribute 'gender'>>> Abey.__dict__Traceback (most recent call last):
  File "<input>", line 1, in <module>AttributeError: 'Student' object has no attribute '__dict__'

可以看到,在定义了__slots__变量后,Student类实例已经不能随意创建不在__slots__定义内的属性gender,同时实例中也不再有__dict__结构。

用法

继承树

__slots__在继承中有两种表现:

  • 子类未声明__slots__时,不继承父类的__slots__,即此时子类实例可以随意赋值属性

  • 子类声明__slots__时,继承父类的__slots__,即此时子类的__slots__为其自身+父类的__slots__

以下面的父类为例:

>>> class Student(object):...     __slots__ = ('name', 'age')...

创建一个子类不声明__slots__,该类实例可以创建父类__slots__限定之外的属性gender

>>> class SubStudent(Student):...     pass... >>> Bob = SubStudent()>>> Bob.gender = 'Male'>>> Bob.__dict__
{'gender': 'Male'}

而创建一个声明__slots__的子类,该类属性则只能创建父类__slots__+自身__slots__限定的属性:

>>> class SubStudent2(Student):...     __slots__ = 'gender'...     
>>> Cathy = SubStudent2()>>> Cathy.gender = 'Female'>>> Cathy.name = 'Cathy'>>> Cathy.teacher = 'Mrs. Wang'Traceback (most recent call last):
  File "<input>", line 1, in <module>AttributeError: 'SubStudent2' object has no attribute 'teacher'

注意:子类的__slots__本身已经继承自父类,无需重复声明父类已声明的属性。例如上例,重复声明会多占用内存空间:

>>> class SubStudent3(Student):...     __slots__ = ('name', 'age', 'gender')...    >>> from sys import getsizeof>>> getsizeof(Student()), getsizeof(SubStudent2()), getsizeof(SubStudent3())
(56, 64, 80)

性能对比

我们为什么要使用__slots__呢?

更快速地赋值属性

参考Stack Overflow回答中给出的数据:

import timeitclass Foo(object): __slots__ = 'foo',class Bar(object): passslotted = Foo()
not_slotted = Bar()def get_set_delete_fn(obj):
    def get_set_delete():
        obj.foo = 'foo'
        obj.foo        del obj.foo    return get_set_delete

得到测试结果为:

>>> min(timeit.repeat(get_set_delete_fn(slotted)))0.2846834529991611>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))0.3664822799983085

可以看到,在相同的环境(Ubuntu)下,slots为Python3.5带来了接近30%的赋值速度提升:

>>> 0.3664822799983085 / 0.28468345299916111.2873325658284342

节约内存空间

由于不使用__dict__存储对象的属性,__slots__在一些场景下能够节约极大的内存空间。具体数据可以查看参考中的回答链接,不赘述。



作者:严北
链接:https://www.jianshu.com/p/9e17a03d08b1


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消