欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python中__slots__属性介绍与基本使用方法

程序员文章站 2022-06-05 20:52:30
简介 在廖雪峰的python网站上,他是这么说的 python是动态语言,它允许程序在执行过程中动态绑定属性或者方法(使用methodtpye)。 某个实例在执行...

简介

在廖雪峰的python网站上,他是这么说的

python是动态语言,它允许程序在执行过程中动态绑定属性或者方法(使用methodtpye)。

某个实例在执行过程中绑定的属性跟方法,仅在该实例中有效,其他同类实例是没有的。

可以通过给class绑定属性/方法,来给所有实例绑定属性/方法:

student.name = ''
student.set_score = set_score

而如果使用__slots__,它仅允许动态绑定()里面有的属性

例如,下面这样会报错

class student():
__slots__ = ('name', 'age')

s1 = student()
s1.name = 'jack' # ok!
s1.score = 123 # error!

但是我觉得很奇怪,仅有这一个作用吗?于是我再查了其他资料,发现这个函数可以很可观地节约内存,下面来一起看看详细的介绍吧。

__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 timeit

class foo(object): __slots__ = 'foo',

class bar(object): pass

slotted = 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.2846834529991611
1.2873325658284342

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

参考

[1] usage of __slots__? ,  aaron hall, stack overflow

推荐阅读

[1] data model , python document

[2] , david_bj, 51cto

[3] , 廖雪峰

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。