您好,登錄后才能下訂單哦!
本篇文章為大家展示了 __slots__如何在Python項目中正確使用,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
準備
正常情況下,創建class的實例后,可以給該實例綁定任何屬性和方法,這就是動態語言的靈活性。首先定義一個class
class A(object): pass
然后創建一個實例,并給實例添加屬性和方法。
a = A() print a.__dict__ #{} A.name = 'xiaoming' #動態的給實例綁定屬性,其實例屬性會保存到實例的__dict__中 print a.__dict__ #{'name': 'xiaoming'} f = lambda :100 a.fun = f print a.__dict__ #{'fun': <function <lambda> at>, 'name': 'xiaoming'}
此時的name屬性和fun()方法只有實例a能使用,類A的其他實例不能使用,如果想讓類A的所有實例都能使用,我們需要給類A綁定方法
print A.__dict__ #... f = lambda :100 A.fun = f print A.__dict__ #... + 'fun': <function <lambda> at 0x0000000003582978>
此時,類A的所有實例就能使用方法fun()了。
​ 通常情況下,上面的fun()方法應該定義在class中,但動態綁定允許在程序運行的過程中動態的給class增加功能,這在靜態語言中很難實現,這也是動態語言的優點。
__slots__
​ 如果在一個類中定義了__slots__屬性,那么這個類的實例將不會擁有__dict__屬性,沒有__dict__的實例也就不能添加實例屬性了。簡單來說,__slots__的作用就是阻止類在實例化時為實例分配__dict__屬性,限制該實例能添加的屬性。
作用
​ 通常情況下實例使用__dict__來存儲自己的屬性,它允許實例動態地添加或刪除屬性。然而,對一些在編譯期就已經知道有什么變量的類或者不允許動態添加變量的類來說,它們并不需要動態地添加變量。如果想要限制實例屬性,不想讓它動態添加屬性怎么辦?比如我們只允許對A的實例添加name和age屬性。
​ 為了達到上述目的,Python允許在定義class的時候,定義一個__slots__變量,來限制該class的實例能添加的屬性。
class A(object): __slots__ = ('age','name') a = A() a.name = 'xiaoming' a.age = 10 a.id = 123456 #error AttributeError: 'A' object has no attribute 'id'
由于id不在__slots__中,所以實例不能添加id屬性。任何試圖給實例添加一個其名不在__slots__中的屬性都將觸發AttributeError異常。
實現原理
__slots__中的變量是類屬性,類型為數據描述符。
#!/usr/bin/python # -*- coding: utf-8 -*- class Foo(object): __slots__ = ('age','name') def __init__(self,age = 0): self.age = age s = Foo.__dict__['age'] print s #<member 'age' of 'Foo' objects> print type(s) #<type 'member_descriptor'> '''證明為數據描述符''' print '__get__' in dir(s) #True print '__set__' in dir(s) #True
__slots__中的變量雖然是類屬性,但是不同實例之間互不影響。因為描述符方法的一個參數為實例,建立一個實例和值的映射還是很簡單的。如果不懂,建議看描述符 。
f1 = Foo(1) f2 = Foo(2) print f1.age,f2.age #1,2 print Foo.__dict__['age'].__get__(f1) #1 print Foo.__dict__['age'].__get__(f2) #2
__slots__的好處
如果類沒有定義__slots__ ,該類的實例會有__dict__屬性,通過__dict__可修改,刪除,增加實例屬性。
如果類定義了__slots__,該類的實例不會有__dict__屬性。實例中的__dict__屬性是非常耗內存的,當創建上百萬個實例的時候,所有實例的__dict__會占用一塊很大的內存。沒有了__dict__的實例也就不能動態添加屬性,只需分配固定的空間來存儲已知的屬性。因此使用__slots__的類能節省一部分內存開銷。
對于不需要動態添加屬性的類來說,應使用__slots__。
注意:不用過早的使用這個方法,它不利于代碼維護,當實例很多(上千萬)時,這種優化才有明顯的效果。在實際使用中,__slots__從未被當作一種安全的特性來使用,它是對內存和執行速度的一種性能優化。使用__slots__的類的實例不再使用字典來存儲實例屬性,而是使用基于數組的一種更加緊湊的數據結構,所以當實例很多時,使用__slots__可以顯著減少內存占用和執行時間。
class A(object): pass class B(object): __slots__ = ('age','name') a = A() b = B()
沒有__slots__的類和實例
print a.__dict__ #{} print A.__dict__ ''' {'__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None} '''
有__slots__的類和實例
print b.__dict__ #AttributeError: 'B' object has no attribute '__dict__' print B.__dict__ ''' {'age': <member 'age' of 'B' objects>, '__module__': '__main__', '__doc__': None, '__slots__': ('age', 'name'), 'name': <member 'name' of 'B' objects>} '''
使用定義__slots__的類的注意事項
class Foo(object): __slots__ = ('age','name')
1.__slots__僅對當前類起作用,對子類是不起作用的,除非在子類中也定義__slots__,這樣,子類允許定義的屬性就是自身的__slots__加上父類的__slots__。
class F1(Foo): pass class F2(Foo): __slots__ = () f1,f2 = F1(),F2() f1.a = 1 f2.a = 1 #AttributeError: 'F2' object has no attribute 'a' f2.age = 1
2.如果實例未給__slots__中的變量賦值,該實例不能使用__slots__中的變量。
f = Foo() print f.age #AttributeError: age f.age = 1 print f.age #1
3.實例將不再擁有__dict__,但是類還是擁有__dict__屬性的,所以還是可以給類增加類屬性的;
f = Foo() Foo.xx = 1 print f.xx #1
4.定義了__slots__后,如果__slots__中的變量為類變量,該變量對于該類的實例來說是只讀的。如果想修改的話,可以通過類來修改。
class Foo(object): __slots__ = ('age','name') age = 10 def __init__(self): self.name = 'xiaoming' f = Foo() print f.name #'xiaoming' print f.age #10 f.name = 'xiaohong' #f.age = 12 #AttributeError: 'Foo' object attribute 'age' is read-only Foo.age = 12 #正確 print f.name #'xiaohong' print f.age #12 #del f.age #AttributeError: 'Foo' object attribute 'age' is read-only #del f.name 調用的是描述符方法__delete__,應該是將描述符中存儲的實例與值的映射刪除了 del f.name #實例屬性的刪除還是可以的,但是刪除的并不是類字典中的name屬性 print f.name #AttributeError: name del Foo.age #通過類來刪除類屬性還是可以的,不過這樣會影響該類的所有實例 print f.age #AttributeError: 'Foo' object has no attribute 'age'
上述內容就是 __slots__如何在Python項目中正確使用,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。