认识Python,深入理解python

  认识Python,深入理解python

  第一条函数2优点2.1节省内存2.2更快的属性访问速度3原理4注意4.1赋值4.2添加__dict__4.3类属性赋值到_ _ slots _ _ 4.4 _ _ slots _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _的限制

  1功能

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

  Class(object):_ _ slots _ _=[ a ]if _ _ name _ _= _ _ main _ :t=test()t . a=1 test . c=3 # Class属性仍然可以用print(t.c) # output: 3t。属性: test 对象没有属性 b 从上面的例子中,我们可以看到__slots__的具体作用。它的作用是约束类实例的属性,不允许类实例随意调用和给实例添加属性。

  优点2.1节省内存当python的类没有定义__slots__,实例的属性管理实际上依赖于字典,也就是神奇的属性__dict__,它实际上是一个字典,存储了实例的所有属性和对应的值。应该注意,定义了__slots__的类实例不再具有__dict__属性。

  在python中,字典的内存分配规则是预先分配一个内存区域,当元素添加到一定阈值时再分配一个更大的内存区域。可以看出__dict__ storage属性会预留更大的空间,所以会有更大的内存浪费。

  __slots__的做法是根据实例创建之初__slots__中声明的属性分配定长内存,实际上是一个定长列表,所以会节省更多内存。

  示例描述:

  from memory_profiler导入profile class TestA(object):_ _ slots _ _=[ a , b , c] def __init__(self,a,b,c):self . a=a self . b=b self . c=cclass TestB(object):def _ _ init _ _(self,a,b,c):self . a=a self . b=b self . c=c @ profile def test():temp=[TestA(I,I,i 1,I 2)for I in range(10000)]del temp temp=.

  可以看出,定义了__slots__的类实例会节省更多的内存。

  总结__slots__在实际使用中应该用到的场景:需要大量创建固定属性的实例时

  2.2更快的属性访问速度使用__slots__访问属性实际上省去了一次减肥的过程,所以属性访问速度会更快。

  示例描述:

  从line_profiler导入LineProfilerslots=[a{} 。format(I)for I in range(10000)]class TestA(object):_ _ slots _ _=slots def _ _ init _ _(self):for I in slots:self。__setattr__(i,i[1:])类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 . a 6666 for I in range(10000):tmp=b . a 6666 if _ _ name _ _== _ _ main _ _ :profiler=line profiler()profiler(test)()profiler . print _ stats()

  3原理当然,理解原理最好的方法就是看源代码,这里就不出丑了。找一个关于__slots__源代码的大博客就很清楚了。如果你真的理解了原理,很多关于__slots__的问题就迎刃而解了。

  下面是一个python版本的__slots__实现来说明这个原理:

  插槽工作方式的粗略近似 class Member(object): Descriptor implementing slot lookup def _ _ init _ _(self,i): self.i=i def __get__(self,obj,type=None): return obj。_ slot values[self . I]def _ _ set _ _(self,obj,value): obj。_ slot values[self . I]=value class Type(Type):检测并实现_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列表并调用原始的__init__ self。_slotvalues=[None] * len(slots)如果original_init不为None: original_init(self,*args,**kwds)命名空间[__init__]=__init__返回类型。__new__(self,name,bases,Namespace)从python实现中我们可以看到,定义__slots__,类会为__slots__中的每个属性创建一个成员实例来记录属性in _ slots值对应值的偏移量,类实例会创建一个定长的list _ slots值来存储对应的属性值。由于使用了定长链表和偏移量,节省了内存和访问时间。

  4使用注释4.1赋值__槽_ _接收可迭代对象的赋值。一般可以用list或者tuple。注意:

  Dict赋值只接受keys();str赋值只有一个属性是赋值的字符串。从源代码中可以知道,__slots__中声明的属性除了__dict__和__weakref__(没错,这两个属性可以包含在__slots__)之外,都是可以重复的(不过除了浪费空间之外没什么用)。

  4.2添加__dict__到__slots__在添加__dict__到__slots__后,可以恢复动态添加属性的功能。但是以前的学生余圣良从来没有这样用过。

  类Test(object):__slots__=[a ,_ _ dict _ _ ]if _ _ name _ _= _ main _ _ :t=Test()t . a= a t . b= b print(t . _ _ dict _ _)# output:{ b :我们知道__slots__中声明的类属性实际上是member_descriptor实例,类实例取值时实际上是在list中寻址实例attribute _slotsvalue。因此,如果class属性被重新赋值,寻址过程将被破坏,instance属性的值将受到影响。

  示例描述:

  class Test1(object):__slots__=[ a ]class Test2(object):passif _ _ name _ _= _ _ main _ _ :x=Test1()x . a= a Test1 . a=1 print(x . a)# Output:1y=Test2()y . a= a test 2 . a=1 print(y . a)# Output: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 _ _)# output:[ x ]print(c . __dict__)# output:{ y :2 } 4 . 4 . 2父类无,子类有继承父类_ _ dict _ _的动态扩展属性并拥有_ _ slot的子类。

  class Parent(object):pass class Child(Parent):__slots__=[ x ]c=Child()c . x,c.y=1,2 print(c . __slots__)# output:[ x ]print(c . _ _ dict _ _)# output:{ y:2 } 4 . 4 . 3父类有,子类有_ _ slots _ _会覆盖父类_ _ slots _ _,子类还可以

  class parent(object):_ _ slots _ _=[ x ]class child(parent):_ _ slots _ _=[ y ]c=child()print(c . _ _ slots _ _)# Output:[ y ]c . x,c C.y) # Output: 1 2 4.4.4多父类继承如果只有一个父类有非空的_ _ slots _ _,其他父类没有或者_ _ slots _ _为空,情况类似于上面的单继承;

  如果多个父类有非空的__slots__,将会报告一个错误。

  类父级(对象):_ _ slots _ _=[ x ]类父级(对象):_ _ slots _ _=[ y ]类子级(父级,父级):#报错:类型错误:多个基础具有实例布局冲突通道推荐阅读:

  __插槽_ _是如何实现的源码分析

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: