文章目录

  • 1 功能
  • 2 优点
    • 2.1 节省内存
    • 2.2 更快的属性访问速度
  • 3 原理
  • 4 使用注意点
    • 4.1 赋值
    • 4.2 向__slots__中添加__dict__
    • 4.3 类属性赋值的限制
    • 4.4 __slots__在继承中的问题
      • 4.4.1 父类有,子类无
      • 4.4.2 父类无,子类有
      • 4.4.3 父类有,子类有
      • 4.4.4 多父类继承

1 功能

__slots__是python类的魔法属性,可接收一个iterable对象作为属性。定义后,该类实例只能创建__slots__中声明的属性,否则报错。

class Test(object):__slots__ = ['a']if __name__ == '__main__':t = Test()t.a = 1Test.c = 3  # 类属性仍然可以自由添加print(t.c)  # 输出:3t.b = 2  # AttributeError: 'Test' object has no attribute 'b'

从上面的例子能看出__slots__的具体功能,它的作用就是用来约束类实例的属性,不允许类实例调用方向实例随意添加属性。

2 优点

2.1 节省内存

python的类在没有定义__slots__时,实例的属性管理其实依赖字典, 也就是魔法属性__dict__,它其实就是个存放实例所有属性及对应值的字典。需要注意的是,定义了__slots__的类实例不再拥有__dict__属性。
在python中字典的内存分配规则是,先预分配一块内存区,当元素添加到一定阈值时进行扩容再分配一块比较大的内存区,由此可见__dict__存储属性会预留比较大的空间,因此会存在比较大的内存浪费。
__slots__的做法就是在创建实例之初就按照__slots__中声明的属性分配定长内存,实际就是定长列表,因此会更加节省内存。
实例说明:

from memory_profiler import profileclass TestA(object):__slots__ = ['a', 'b', 'c']def __init__(self, a, b, c):self.a = aself.b = bself.c = cclass TestB(object):def __init__(self, a, b, c):self.a = aself.b = bself.c = c@profile
def test():temp = [TestA(i, i+1, i+2) for i in range(10000)]del temptemp = [TestB(i, i+1, i+2) for i in range(10000)]del tempif __name__ == '__main__':test()


可以看到定义了__slots__的类实例会更加节省内存。
总结一下实际使用中应该使用__slots__的场景:需要大量创建固定属性的实例时

2.2 更快的属性访问速度

使用__slots__访问属性,实际节省了一次哈希的过程,因此属性访问速度会更快一些。
实例说明:

from line_profiler import LineProfilerslots = ['a{}'.format(i) for i in range(10000)]class TestA(object):__slots__ = slotsdef __init__(self):for i in slots:self.__setattr__(i, i[1:])class TestB(object):def __init__(self):for i in slots:self.__setattr__(i, i[1:])def test():a = TestA()b = TestB()for i in range(10000):tmp = a.a6666for i in range(10000):tmp = b.a6666if __name__ == '__main__':profiler = LineProfiler()profiler(test)()profiler.print_stats()

3 原理

理解原理的最好方法当然是阅读源码,这里本人就不献丑了,找到一篇大佬的博客对于__slots__源码讲解的还蛮清晰,真正理解了原理,很多关于__slots__的问题也就迎刃而解了。
这里给出一个python版的__slots__实现,用以说明原理:

'Rough approximation of how slots work'class Member(object):'Descriptor implementing slot lookup'def __init__(self, i):self.i = idef __get__(self, obj, type=None):return obj._slotvalues[self.i]def __set__(self, obj, value):obj._slotvalues[self.i] = valueclass Type(type):'Metaclass that detects and implements _slots_'def __new__(self, name, bases, namespace):slots = namespace.get('_slots_')if slots:for i, slot in enumerate(slots):namespace[slot] = Member(i)original_init = namespace.get('__init__')                def __init__(self, *args, **kwds):'Create _slotvalues list and call the original __init__'                self._slotvalues = [None] * len(slots)if original_init is not None:original_init(self, *args, **kwds)namespace['__init__'] = __init__return type.__new__(self, name, bases, namespace)

从python实现中,我们可以看到,定义__slots__之后,类会为__slots__中每个属性创建一个Member实例用来记录属性对应值在_slotvalues中的偏移量,类实例会创建一个定长列表_slotvalues用来存储对应的属性值。因为使用定长列表和偏移量所以也就更省内存和访问时间。

4 使用注意点

4.1 赋值

__slots__接收可迭代对象赋值,一般使用list或者tuple即可。注意:

  1. dict赋值只会取keys();
  2. str赋值只会有一个属性就是赋值的字符串。

从源码中可以知道,__slots__中声明的属性可以重复(然而除浪费空间之外没什么卵用)除了__dict__和__weakref__之外(是的,__slots__之中还可以包含这两个属性)。

4.2 向__slots__中添加__dict__

在__slots__中添加__dict__之后可以恢复动态添加属性功能,然而既生瑜何生亮,从来没这么用过。

class Test(object):__slots__ = ['a', '__dict__']if __name__ == '__main__':t = Test()t.a = 'a't.b = 'b'print(t.__dict__)  # 输出:{'b': 'b'}

4.3 类属性赋值的限制

从__slots__原理的介绍中,我们知道__slots__之中声明的类属性其实是member_descriptor实例,类实例在取值时其实时对于实例属性_slotsvalue做列表寻址。因此如果对类属性重新赋值,会破坏寻址过程,影响实例属性取值。
实例说明:

class Test1(object):__slots__ = ['a']class Test2(object):passif __name__ == '__main__':x = Test1()x.a = 'a'Test1.a = 1print(x.a)  # 输出:1y = Test2()y.a = 'a'Test2.a = 1print(y.a)  # 输出:a

4.4 __slots__在继承中的问题

在继承中使用__slots__是比较混乱的,情况根据父类子类有没有定义__slots__分为以下几种情况:

4.4.1 父类有,子类无

子类实例继承父类__slots__中的属性,同时也会自动创建__dict__用来动态拓展属性。

class Parent(object):__slots__ = ['x']class Child(Parent):passc = Child()
c.x, c.y = 1, 2
print(c.__slots__)  # 输出:['x']
print(c.__dict__)  # 输出:{'y': 2}

4.4.2 父类无,子类有

子类继承父类__dict__可动态拓展属性,自身__slots__中属性不变。

class Parent(object):passclass Child(Parent):__slots__ = ['x']c = Child()
c.x, c.y = 1, 2
print(c.__slots__)  # 输出:['x']
print(c.__dict__)  # 输出:{y: 2}

4.4.3 父类有,子类有

子类__slots__会覆盖父类__slots__,子类仍可访问父类slots中有但自己没有的属性。

class Parent(object):__slots__ = ['x']class Child(Parent):__slots__ = ['y']c = Child()
print(c.__slots__)  # 输出:['y']
c.x, c.y = 1, 2
print(c.x, c.y)  # 输出:1 2

4.4.4 多父类继承

若只有一个父类有非空__slots__,其他父类无或__slots__为空,则情况同上面单继承类似;
若多个父类有非空__slots__,则会报错。

class Parent(object):__slots__ = ['x']class ParentA(object):__slots__ = ['y']class Child(Parent, ParentA):  # 报错:TypeError: multiple bases have instance lay-out conflictpass

推荐阅读:

  1. HOW __SLOTS__ ARE IMPLEMENTED
  2. python slots源码分析

Python深入理解__slots__相关推荐

  1. Python字典理解

    本文翻译自:Python Dictionary Comprehension Is it possible to create a dictionary comprehension in Python ...

  2. python基础——使用__slots__

    python基础--使用__slots__ 正常情况下,当我们定义了一个class,创建了一个class的实例后,我们可以给该实例绑定任何属性和方法,这就是动态语言的灵活性.先定义class: cla ...

  3. python中的列表理解_掌握『Python列表理解』需要知道的9件事,你知道了吗?

    越来越多的人开始学习Python,它已经成为最流行的编程语言之一,这几乎发生在所有领域.比如网络开发.科学计算,当然还有人工智能. 无论想用python干什么,都绕不开学习Python的数据结构.变量 ...

  4. 我对python的理解_python高级函数以及我对python的理解

    常见的高级函数:lambda.map.reduce.filter.list comprehension lambda 匿名函数,限制一个表达式 m = lambda x,y:x+y # 5 m(2,3 ...

  5. python self理解_Python列表理解

    python self理解 In our previous tutorial we learned about Python Sort List. In this tutorial we will l ...

  6. Python 代码理解 polygon.py

    Python 代码理解 polygon.py 1.运行和阅读代码 该代码的主要功能为通过调用不同的包,设置不同的线段数.线段长度和角度等条件,进行不同图形的绘制. 经过环境配置和代码阅读后,初步运行代 ...

  7. Python优化之__slots__

    目录 类中的__dict__ 掀起字典的盖头来 开始主题:__slots__ 为什么__slots__可以 更详细一点的使用方法 后续计划 类中的__dict__ 每个python类都是隐形的继承自o ...

  8. python yield理解_对Python中Yield的理解

    看到下面这段程序的时候,有点不明白这个yield到底是个啥东西,看了网上很多的博客,大致理解了yield的含义,所以记录下来. 要说yield首先要说python中的生成器,那么什么是生成器? 假设有 ...

  9. python类的__slots__属性、__del__属性、上下文(__enter__和__exit__)、

    常规情况下,类的属性字典是共享的,而实例的字典是独立的.如果一个类的属性较少,但是拥有很多的实例,这些实例的属性字典会占用较多的内存空间.对这样的类来说,为了节省内存空间,可以使用__slots__类 ...

最新文章

  1. **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java版)
  2. 第三轮316工程知识竞赛简讯_【资讯】第五届日本文化知识竞赛决赛
  3. linux之tr命令使用和总结
  4. [VSCode] 设置 pylint 以解决 Module ‘torch‘ has no ‘xxx‘ member
  5. ecshop根目录调用_ecshop调用指定商品分类下的商品
  6. Velocity - 单例还是非单例
  7. automak 和 autoconf 介绍
  8. tp5实现126邮件服务
  9. FTP服务器是什么意思
  10. 深度学习数学基础 讲义_深度学习入门部分的讲义3
  11. windows7计算机不显示u盘,win7插上u盘不显示盘符怎么办|win7 u盘识别不显示盘符的解决方法...
  12. DAMA数据管理知识体系指南之数据安全管理
  13. kso经验积累 -- c#发送邮件
  14. 《Python编程 从入门到实践》第八章 ——函数习题
  15. 网络软件测试的IP地址,如何快速查出网内空闲IP地址
  16. Cesium,mars3d,3dtiles 3d地图显示
  17. 5月编程排行榜出炉,最佳编程语言是谁?
  18. coffeescript 汉字转拼音代码
  19. 【Python计量】DID模型构建
  20. 微信高音质speex格式转为mp3详细教程

热门文章

  1. MySQL使用IN、EXISTS、ANY、ALL关键字的子查询
  2. 慌的一批!妹子一个rm -rf把公司服务器数据删没了...
  3. java学习之前端基础
  4. 职场上班族可吃零食能消除疲劳
  5. Swing组件组合框
  6. 学校校车运营各项安全管理制度_学校校车安全管理制度汇编
  7. Chromedriver、geckodriver、IEDriverServer与浏览器的对应版本和下载地址
  8. Windows平台崩溃转储系统crashrpt的使用
  9. 【毕业设计之python系列】基于django的奶茶店管理系统
  10. EIDchain公链:区块链数据隐私保护解决方案