6.1一切皆對象
運算符
當我們建立兩個列表對象相加時介粘,實際上是調(diào)用了__add()__特殊方法,而這些功能相同的運算符使程序更加簡潔玫霎。
乘法對應(yīng)的特殊方法是__mul__(),重归、or邏輯或運算符對應(yīng)的方法是__or__(),至壤。
改變在這些與運算符相關(guān)的對應(yīng)特殊方法可以改變運算的方式鹏控。例如:列表中并沒有關(guān)于列表對象的減法操作致扯,我們可以通過修改特殊方法來定義,如下
class SuperList(list):
def __sub__(self,b):
a = self[:] #繼承了list当辐,self可以通過[:]的引用來表示整個列表
b = b[:]
while len(b) > 0:
element_b = b.pop() #去掉b列表中的元素并將其返回抖僵。
if element_b in a:
a.remove(element_b)
return a
print(SuperList([1,2,3])) - SuperList(([3,4])) #輸出[1,2]
元素引用
我們常用的元素引用,其實也是調(diào)用特殊方法瀑构,如:
li[3]其實是調(diào)用了__getitem__()方法
內(nèi)置函數(shù)的實現(xiàn)
許多內(nèi)置函數(shù)的實現(xiàn)也是調(diào)用了對象的特殊方法裆针,如:
len()函數(shù)是調(diào)用了__len__()方法
而abs()方法是調(diào)用了__abs()__方法,int()調(diào)用了__int__()方法
6.2屬性管理
屬性覆蓋的背后
python中的__dict__屬性寺晌,會記錄一個類或?qū)ο髶碛械膶傩浴?strong>__dict__也可以從祖先類繼承。例如:一個“鳥”類澡刹,下面有一個子類“雞”類呻征,我們創(chuàng)建了這個“雞”類的一個對象summer,那么這個對象就繼承了“雞”類和“鳥”類的屬性罢浇,還擁有自身的屬性陆赋。
當我們在 調(diào)用屬性的時候,會從_\dict__中記錄的對象向下遍歷嚷闭,調(diào)用優(yōu)先遇到的屬性攒岛。其遍歷順序summer/chicken/Bird/object(屬性是分層管理的)。
【注】而當我們賦值的時候胞锰,python不會分層查找灾锯,直接在summer的__dict__的記錄中尋找,若沒有對象屬性嗅榕,則創(chuàng)建一個新的屬性并賦值顺饮;使用self引用對象也要準守相同規(guī)則。
特性
特性:即時生成屬性的方法凌那,通過內(nèi)置函數(shù)property()來創(chuàng)建兼雄。
【注:】property()有四個參數(shù),分別用于設(shè)置獲取帽蝶、修改赦肋、刪除時的特性,最后一個參數(shù)是特性的文檔,起說明作用
class num(object):
def __init__(self,value):
self.value = value
def get_neg(self):
return -self.value
def set_neg(self, value):
self.value = -value
def del_neg(self):
print("vlaue also deleted")
del self.value
neg = property(get_neg, set_neg, del_neg, "I'm negative")
x = num(1.1)
print(x.neg) #輸出1.1
x.neg = -22
print(x.value) #輸出22
print(num.neg__doc__) #輸出"I'm negative"
del x.neg
___getattr__()方法
上面我們接受了即時生成的屬性方法佃乘,那么___getattr__()方法則是來查詢即時生成的屬性局蚀。
我們調(diào)用一個屬性時,如果__dict__無法找到該屬性恕稠,則會調(diào)用___getattr__()來即時生成該屬性琅绅。
?????【注:】該方法只能用于查詢不在__dict__系統(tǒng)中的屬性,若是在則會報錯鹅巍。
__setattr__(self,name,value)用來修改任意屬性千扶;__delattr__(self,name)用來刪除任意屬性;__getattribute__()用來查詢?nèi)我鈱傩浴?/p>
6.3我是風(fēng)兒骆捧,我是沙
動態(tài)類型
python中的變量不需要聲明類型澎羞,可以重新賦任何值。
因為對象實際上是存在在內(nèi)存中的實體敛苇,有自己的內(nèi)存地址妆绞,而對象名只是指向某一內(nèi)存地址從而來引用該對象,當我們將對象名重新賦值時枫攀,只是將它指向其他地址括饶,因此可以重新賦任何值。
a = 3
print(id(a)) #輸出的是3的地址
a = "at"
print(id(a)) #輸出的是“at”的地址
我們也可以通過is來判斷兩個引用是否指向同一個地址:
a = 3
b = 3
print(a is b) #輸出True
可變與不可變對象
a = 5
print(id(5))
b = a
print(id(a)) #輸出5的地址
print(id(b)) #輸出5的地址
a = a + 2
print(id(a)) #輸出7的地址
print(id(7)) #輸出7的地址
print(id(b)) #輸出5的地址
通過上面的例子我們發(fā)現(xiàn)改變一個引用来涨,并不影響其他引用图焰。因為它的改變并沒有改變對象本身。
list2 = [1,2,3]
list1 = list2
list1[0] = 10
print(list2) #輸出[10,2,3]
【注:】這里的改變并不是改變list1的指向蹦掐,而是直接對列表對象進行操作技羔,所以會導(dǎo)致其他引用也改變。
對于這種自身會發(fā)生改變的對象卧抗,稱為可變對象藤滥。不能改變自身的對象,賦值時只改變引用的指向社裆,這種對象稱為不可變對象拙绊。
【注:】整數(shù)、浮點數(shù)浦马、字符串和元組都是不可變對象
從動態(tài)類型看函數(shù)的參數(shù)傳遞
函數(shù)的傳遞本質(zhì)上傳遞的是引用时呀,如:
def f(x):
print(id(x))
x = 100
print(id(x))
a = 1
print(id(a))
f(a)
print(id(a))
【注:】如果a是不可變對象則x的賦值不影響a,若a是可變對象晶默,則函數(shù)內(nèi)部的賦值操作會影響a的值谨娜。
6.4內(nèi)存管理
引用管理
引用計數(shù):一個對象可以有多個引用,而每個對象中都存有指向該對象的引用總數(shù)磺陡。
我們可以使用sys包中的getrefcount()來查看某個對象的引用趴梢。
from sys import getrefcount
a = [1,2,3]
print(getrefcount(a)) #返回2
b = a
print(getrefcount(b)) #返回3
【注:】我們在使用某個引用作為參數(shù)傳遞給getrefcount()時漠畜,實際上創(chuàng)建了一個臨時性的引用,所以結(jié)果會比實際引用多1坞靶。
對象引用對象
列表和字典這些可變對象它們其實都是數(shù)據(jù)容器對象憔狞,本身可以包含多個對象;但是容器對象中包含的并不是元素對象本身而是指向各個元素對象的引用彰阴。所以我們可以通過對象來引用對象瘾敢。
class from_obj(object):
def_init_(self, to_obj):
self.to_obj = ob_obj
b = [1,2,3]
a = from_obj(b)
print(id(a.to_obj))
print(id(b))
【注:】容器中的引用會構(gòu)成很復(fù)雜的拓撲結(jié)構(gòu),我們可以使用objgraph包來繪制其引用關(guān)系尿这。
對象間相互引用會構(gòu)成所謂的引用環(huán)簇抵。
我們也可以通過del關(guān)鍵字來刪除某個引用。
垃圾回收
當python中某個對象的引用計數(shù)為0時射众,該對象就會被垃圾回收碟摆。
python只在特定條件下自動啟動垃圾回收,當python運行時叨橱,會記錄分配對象和取消分配對象的次數(shù)典蜕,當兩者差值高于某個闕值時,垃圾回收才會啟動罗洗。
我們可以通過gc模塊的get_threshold()方法來查看該闕值愉舔。
python中還采用了分代回收的策略,剛建立的對象為0代栖博,經(jīng)歷過垃圾回收還存活的則歸入下一代屑宠,共有三代,分別為0,1,2三代仇让。0代經(jīng)歷過一定次數(shù)的垃圾回收后就會啟動對1代的掃描清理。
import gc
print(gc.get_threshold())
若返回(700, 10, 10)躺翻,700指垃圾回收啟動的闕值丧叽,第一個10指0代經(jīng)歷十次垃圾回收則啟動對1代的掃描處理,第二個十指1代經(jīng)歷了十次垃圾回收后啟動對2代的垃圾回收公你。
孤立的引用環(huán)
a = []
b = a
a.append(b)
del a
del b
上面的代碼a,b對象彼此互相引用構(gòu)成一個引用環(huán)踊淳,在刪除a,b后由于引用環(huán)的存在使得對象引用計數(shù)沒有降為一,所以不會被垃圾回收陕靠。
為此迂尝,python會復(fù)制每個對象的引用計數(shù),可以記為gc_ref,假設(shè)每個對象i剪芥,計數(shù)為gc_ref_i垄开。
Python會遍歷所有對象i對對象j的引用,將相應(yīng)的gc_ref_j減一税肪,在結(jié)束遍歷后gc_ref不為0的對象以及這些對象的引用對象則保留溉躲,其他則回收榜田。
python采用了一種相對簡單的垃圾回收機制,即引用計數(shù)锻梳。