參考鏈接: Python的__name __(特殊變量)
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__來優化內存。
?提醒:不要貿然進行這個優化,把它用在所有地方。這種做法不利于代碼維護,而且只有生成數以千計的實例的時候才會有明顯效果。
?
?(完)