簡(jiǎn)介
__slots__
允許我們聲明并限定類成員腿准,并拒絕類創(chuàng)建__dict__
和__weakref__
屬性以節(jié)約內(nèi)存空間叙量。
Python是動(dòng)態(tài)語(yǔ)言信轿,對(duì)于普通的類,可以為類實(shí)例賦值任何屬性缝彬,這些屬性會(huì)存儲(chǔ)在__dict__
中:
>>> class Student(object):
... pass
...
>>> Abey = Student()
>>> Abey.name = 'Abey'
>>> Abey.__dict__
{'name': 'Abey'}
這樣的特性帶來(lái)兩個(gè)問(wèn)題:
- 數(shù)據(jù)通過(guò)字典(Hash)存儲(chǔ)所占用的空間較大
- 如何禁止隨意生成類屬性
當(dāng)然,__slots__
就能解決這兩個(gè)問(wèn)題哺眯。通過(guò)__slots__
屬性限定類屬性的創(chuàng)建:
>>> 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類實(shí)例已經(jīng)不能隨意創(chuàng)建不在__slots__
定義內(nèi)的屬性gender,同時(shí)實(shí)例中也不再有__dict__
結(jié)構(gòu)一疯。
用法
繼承樹(shù)
__slots__
在繼承中有兩種表現(xiàn):
- 子類未聲明
__slots__
時(shí)撼玄,不繼承父類的__slots__
,即此時(shí)子類實(shí)例可以隨意賦值屬性 - 子類聲明
__slots__
時(shí)墩邀,繼承父類的__slots__
掌猛,即此時(shí)子類的__slots__
為其自身+父類的__slots__
以下面的父類為例:
>>> class Student(object):
... __slots__ = ('name', 'age')
...
創(chuàng)建一個(gè)子類不聲明__slots__
,該類實(shí)例可以創(chuàng)建父類__slots__
限定之外的屬性gender:
>>> class SubStudent(Student):
... pass
...
>>> Bob = SubStudent()
>>> Bob.gender = 'Male'
>>> Bob.__dict__
{'gender': 'Male'}
而創(chuàng)建一個(gè)聲明__slots__
的子類眉睹,該類屬性則只能創(chuàng)建父類__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__
本身已經(jīng)繼承自父類荔茬,無(wú)需重復(fù)聲明父類已聲明的屬性。例如上例竹海,重復(fù)聲明會(huì)多占用內(nèi)存空間:
>>> class SubStudent3(Student):
... __slots__ = ('name', 'age', 'gender')
...
>>> from sys import getsizeof
>>> getsizeof(Student()), getsizeof(SubStudent2()), getsizeof(SubStudent3())
(56, 64, 80)
性能對(duì)比
我們?yōu)槭裁匆褂?code>__slots__呢慕蔚?
更快速地賦值屬性
參考Stack Overflow回答中給出的數(shù)據(jù):
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
得到測(cè)試結(jié)果為:
>>> min(timeit.repeat(get_set_delete_fn(slotted)))
0.2846834529991611
>>> min(timeit.repeat(get_set_delete_fn(not_slotted)))
0.3664822799983085
可以看到,在相同的環(huán)境(Ubuntu)下斋配,slots為Python3.5帶來(lái)了接近30%的賦值速度提升:
>>> 0.3664822799983085 / 0.2846834529991611
1.2873325658284342
節(jié)約內(nèi)存空間
由于不使用__dict__
存儲(chǔ)對(duì)象的屬性坊萝,__slots__
在一些場(chǎng)景下能夠節(jié)約極大的內(nèi)存空間。具體數(shù)據(jù)可以查看參考中的回答鏈接许起,不贅述十偶。
參考
[1] Usage of __slots__?, Aaron Hall, Stack Overflow
推薦閱讀
[1] Data model, Python Document
[2] python __slots__ 使你的代碼更加節(jié)省內(nèi)存, david_bj, 51CTO
[3] 使用__slots__, 廖雪峰