轉自:http://blog.csdn.net/sxingming/article/details/52892640
python中的new-style class要求繼承Python中的一個內建類型,一般繼承object,也可以繼承list或者dict等其他的內建類型。
在python新式類中,可以定義一個變量__slots__,它的作用是阻止在實例化類時為實例分配dict,
默認情況下每個類都會有一個dict,通過__dict__訪問,這個dict維護了這個實例的所有屬性,舉例如下:
- class base(object):
- var=9 #類變量
- def __init__(self):
- pass
- b=base()
- print b.__dict__
- b.x=2 #添加實例變量
- print b.__dict__
運行結果:
{ }
{'x': 2}
可見:實例的dict只保持實例的變量,對於類的屬性是不保存的,類的屬性包括變量和函數。
由於每次實例化一個類都要分配一個新的dict,因此存在空間的浪費,因此有了__slots__。
__slots__是一個元組,包括了當前能訪問到的屬性。
當定義了slots后,slots中定義的變量變成了類的描述符,相當於java,c++中的成員變量聲明,
類的實例只能擁有slots中定義的變量,不能再增加新的變量。注意:定義了slots后,就不再有dict。如下:
- class base(object):
- __slots__=('x')
- var=8
- def __init__(self):
- pass
- b=base()
- b.x=88 #添加實例變量
- print b.x
- #b.y=99 #無法添加slots之外的變量 (AttributeError: 'base' object has no attribute 'y')
- #print b.__dict__ #定義了__slots__后,就不再有__dict__ (AttributeError: 'base' object has no attribute '__dict__')
運行結果:
88
如果類變量與slots中的變量同名,則該變量被設置為readonly!!!如下:
- class base(object):
- __slots__=('y')
- y=22 # y是類變量,y與__slots__中的變量同名
- var=11
- def __init__(self):
- pass
- b=base()
- print b.y
- print base.y
- #b.y=66 #AttributeError: 'base' object attribute 'y' is read-only
運行結果:
22
22
Python是一門動態語言,可以在運行過程中,修改實例的屬性和增刪方法。一般,任何類的實例包含一個字典__dict__,
Python通過這個字典可以將任意屬性綁定到實例上。有時候我們只想使用固定的屬性,而不想任意綁定屬性,
這時候我們可以定義一個屬性名稱集合,只有在這個集合里的名稱才可以綁定。__slots__就是完成這個功能的。
- class test_slots(object):
- __slots__='x','y'
- def printHello(self):
- print 'hello!'
- class test(object):
- def printHello(self):
- print 'hello'
- print dir(test_slots) #可以看到test_slots類結構里面包含__slots__,x,y
- print dir(test)#test類結構里包含__dict__
- print '**************************************'
- ts=test_slots()
- t=test()
- print dir(ts) #可以看到ts實例結構里面包含__slots__,x,y,不能任意綁定屬性
- print dir(t) #t實例結構里包含__dict__,可以任意綁定屬性
- print '***************************************'
- ts.x=11 #只能綁定__slots__名稱集合里的屬性
- t.x=12 #可以任意綁定屬性
- print ts.x,t.x
- ts.y=22 #只能綁定__slots__名稱集合里的屬性
- t.y=23 #可以任意綁定屬性
- print ts.y,t.y
- #ts.z=33 #無法綁定__slots__集合之外的屬性(AttributeError: 'test_slots' object has no attribute 'z')
- t.z=34 #可以任意綁定屬性
- print t.z
運行結果:
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'printHello', 'x', 'y']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'printHello']
**************************************
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'printHello', 'x', 'y']
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'printHello']
***************************************
11 12
22 23
34
正如上面所說的,默認情況下,Python的新式類和經典類的實例都有一個dict來存儲實例的屬性。這在一般情況下還不錯,而且非常靈活,
乃至在程序中可以隨意設置新的屬性。但是,對一些在”編譯”前就知道有幾個固定屬性的小class來說,這個dict就有點浪費內存了。
當需要創建大量實例的時候,這個問題變得尤為突出。一種解決方法是在新式類中定義一個__slots__屬性。
__slots__聲明中包含若干實例變量,並為每個實例預留恰好足夠的空間來保存每個變量;這樣Python就不會再使用dict,從而節省空間。
【使用memory_profiler模塊,memory_profiler模塊是在逐行的基礎上,測量代碼的內存使用率。盡管如此,它可能使得你的代碼運行的更慢。使用裝飾器@profile來標記哪個函數被跟蹤。】
下面,我們看一個例子:
- from memory_profiler import profile
- class A(object): #沒有定義__slots__屬性
- def __init__(self,x):
- self.x=x
- @profile
- def main():
- f=[A(523825) for i in range(100000)]
- if __name__=='__main__':
- main()
運行結果,如下圖:
第2列表示該行執行后Python解釋器的內存使用情況,第3列表示該行代碼執行前后的內存變化。
在沒有定義__slots__屬性的情況下,該代碼共使用了20.8MiB內存。
從結果可以看出,內存使用是以MiB為單位衡量的,表示的mebibyte(1MiB = 1.05MB)
- from memory_profiler import profile
- class A(object):#定義了__slots__屬性
- __slots__=('x')
- def __init__(self,x):
- self.x=x
- @profile
- def main():
- f=[A(523825) for i in range(100000)]
- if __name__=='__main__':
- main()
運行結果,如下圖:
可以看到,在定義了__slots__屬性的情況下,該代碼共使用了6.1MiB內存,比上面的20.8MiB節省了很多內存!
綜上所述,在確定了類的屬性固定的情況下,可以使用__slots__來優化內存。
提醒:不要貿然進行這個優化,把它用在所有地方。這種做法不利於代碼維護,而且只有生成數以千計的實例的時候才會有明顯效果。