標籤:and getattr 尋找 需要 for 異常處理 類執行個體化 統一 注意
一、
__getattribute__
object.__getattribute__(self, name)
無條件被調用,通過執行個體訪問屬性。如果class中定義了__getattr__()
,則__getattr__()
不會被調用(除非顯示調用或引發AttributeError異常)
class Foo: ? def __init__(self,x): self.x = x ? def __getattr__(self, item): print("執行__getattr__") ? def __getattribute__(self, item): print("執行__getattribute__") # raise AttributeError("不存在的異常") ? f = Foo("nick") print(f.x) print(f.xxxx)#這裡本來調用後應該在__getattribute__中產生AttributeError,然後調用__getattr__,但在#這裡__getattribute__被重寫了,沒有產生AttributeError,所以就沒調用__getattr__
輸出結果
執行__getattribute__ None 執行__getattribute__ None
object.__getattr__(self, name)
當一般位置找不到attribute的時候,會調用getattr,返回一個值或AttributeError異常。
class Foo: ? def __init__(self,x): self.x = x ? def __getattr__(self, item): print("執行__getattr__") ? # def __getattribute__(self, item): # print("執行__getattribute__") # raise AttributeError("不存在的異常") ? f = Foo("nick") print(f.x) print(f.xxxx) #這裡是執行了新定義的__getattr__方法,並沒有直接報錯
輸出結果
nick 執行__getattr__ None
每次通過執行個體訪問屬性,都會經過__getattribute__
函數。而當屬性不存在時,仍然需要訪問__getattribute__
,不過接著要訪問__getattr__
。這就好像是一個異常處理函數。 需要注意的是,當使用類訪問不存在的變數是,不會經過__getattr__
函數。
當屬性存在時,經過__getattribute__
函數,返回屬性的值。
二、
__setitem__,__getitem,__delitem__
在類中,對象以object["key"]形式訪問屬性或方法時調用item系列內建方法,對象以object.屬性(以點的形式)調用屬性或方法時調用attr系列內建方法。
class Foo: ? def __init__(self,name): self.name = name ? def __getitem__(self, item): print("執行__getitem__") return self.__dict__[item] ? def __setitem__(self, key, value): print("執行__setitem__") self.__dict__[key] = value ? def __delitem__(self, key): print("執行__delitem__") self.__dict__.pop(key) ? f = Foo("nick") print(f["name"]) #擷取屬性, 執行__getitem__方法 f["age"] = 18 #設定屬性,執行__setitem__方法 print(f.__dict__) del f["name"] #刪除屬性,執行__delitem__方法 print(f.__dict__)
輸出結果
執行__getitem__ nick 執行__setitem__ {‘name‘: ‘nick‘, ‘age‘: 18} 執行__delitem__ {‘age‘: 18}
三、
__str__,__repr__
,
__format__
__str__
是控制改變對象的字串顯示
在print(obj)/‘%s‘%obj/str(obj)
時,實際上是內部調用了obj.__str__
方法,如果str方法有,那麼他返回的必定是一個字串,如果沒有__str__
方法,會先找本類中的__repr__
方法,再沒有再找父類(object)中的__str__
。
例子1
class Foo: def __str__(self): return "控制列印對象時的顯示形式" ? f = Foo() print(f) #輸出結果: 控制列印對象時的顯示形式
分析:這裡列印對象直接調用了被重新定義的__str__
方法
例子2
class Foo: def __str__(self): return "控制列印對象時的顯示形式" ? f = Foo() a = str(f) print(a) #輸出結果: 控制列印對象時的顯示形式
分析:這裡的str(object)也是調用了__str__
方法
例子3
class Foo: def __str__(self): return "控制列印對象時的顯示形式" ? f = Foo() print("%s"%f) #輸出結果: 控制列印對象時的顯示形式
分析:這裡的“%s”%f也是調用了__str__
方法
__repr__
也是顯示對象的名字方式,用repr(object)或者“%r”%object會調用__repr__
方法
repr 是str的備胎,但str不能做repr的備胎
例子4
class Foo: ? def __str__(self): return "控制列印對象時的顯示形式" ? def __repr__(self): print("在執行repr") return "repr Foo" pass f = Foo() print("%r"%f) print(f)
輸出結果
在執行repr repr Foo 控制列印對象時的顯示形式
分析:這裡print("%r"%f) 調用重新定義的__repr__
方法,print(f)調用__str__
方法
例子5
class Foo: ? def __str__(self): return "控制列印對象時的顯示形式" ? def __repr__(self): print("在執行repr") return "repr Foo" pass f = Foo() print(repr(f))
輸出結果
在執行repr repr Foo
例子6
class Foo: ? # def __str__(self): # return "控制列印對象時的顯示形式" ? def __repr__(self): print("在執行repr") return "repr Foo" pass f = Foo() print(f)
輸出結果
在執行repr repr Foo
分析:如果要調用__str__
方法,首先在本類中尋找有沒有__str__
方法,如果沒有就找本類有沒有__repr__
方法,如果有就執行__repr__
方法,不再借用父類的__str__
方法。所以repr 是str的備胎,但str不能做repr的備胎。
例子7
class Foo: ? def __str__(self): return "控制列印對象時的顯示形式" ? # def __repr__(self): # print("在執行repr") # return "repr Foo" pass f = Foo() print(repr(f)) #輸出結果 <__main__.Foo object at 0x002EB550>
分析:要調用__repr__
方法,本類沒有,直接借用父類的__repr__
方法,而不執行__str__
方法。所以repr 是str的備胎,但str不能做repr的備胎。
__format__
自定製格式化字串__format__
,
format_dic = { "ymd":"{0.year}{0.month}{0.day}", "dmy":"{0.day}:{0.month}:{0.year}", "mdy":"{0.month}-{0.day}-{0.year}" } ? class Foo: ? def __init__(self,year,month,day): self.year = year self.month = month self.day = day ? def __format__(self, format_spec): if not format_spec or format_spec not in format_dic: format_spec = "ymd" fmt = format_dic[format_spec] return fmt.format(self) ? f = Foo(2018,8,20) print(format(f)) print(format(f,"dmy")) print(format(f,"mdy")) print("{:mdy}".format(f))
輸出結果
2018820 20:8:2018 8-20-2018 8-20-2018
分析:調用format方法自定製字串格式化,這裡調用__format__
方法
四、
__slots__
__slots__
:其實就是將類中的名稱鎖定,執行個體化對象,只可以賦值和調用,不可以刪除屬性和增加新的屬性
1、__slots__
是什麼:是一個類變數,變數值可以是列表,元祖,或者可迭代對象,也可以是一個字串(也就是意味著所有執行個體只有一個資料屬性)
2、使用點來訪問屬性本質就是在訪問類或者對象的__dict__
屬性字典(類的字典是共用的,而每個執行個體的是獨立的)
3、字典會佔用大量記憶體,如果你有一個屬性很少的類,但是有很多執行個體,為了節省記憶體可以使用__slots__
取代執行個體的__dict__
當你定義__slots__
後,__slots__
就會為執行個體使用一種更加緊湊的內部表示。執行個體通過一個很小的固定大小的數組來構建,而不是為每個執行個體定義一個字典,這跟元組或列表很類似。在__slots__
中列出的屬性名稱在內部被映射到這個數組的指定小標上。使用__slots__
一個不好的地方就是我們不能再給執行個體添加新的屬性了,只能使用在__slots__
中定義的那些屬性名稱。
4、應用情境:當執行個體化幾萬個對象的時候,每個對象都會產生一個名稱空間__dict__
,而每一個名稱空間都會各自佔用一個記憶體,造成記憶體的浪費,用 __slots__
,不用再產生每個對象的 dict 了,省記憶體,對象的 dict 都統一用類的 dict,屬性都是用 slots 給定義的
class Foo: ? __slots__ = ["name","age"] #在類中定義屬性"name"、"age" ? f1 = Foo() f1.name = "nick" #首先要進行賦值,不賦值調用會出錯 print(f1.name) # f1.gender = "female" # 新增__slots__之外的屬性會報錯 # del f1.age #刪除屬性也會報錯
五、
__doc__
__doc__
顯示文檔注釋資訊,文檔注釋資訊無法繼承給子類。
例子
class Foo: """ 我是文檔注釋 """ pass ? f = Foo() print(f.__doc__) #輸出結果 : 我是文檔注釋
例子2
class Foo: """ 我是文檔注釋 """ pass ? class Son(Foo): pass ? s = Son() print(s.__doc__) #輸出結果:None
六、
__module__
和
__class__
__module__
表示當前操作的對象在那個模組
__class__
表示當前操作的對象的類是什麼
例子1
class Foo: ? def __init__(self,name): self.name = name ? f = Foo("nick") print(f.__module__) #當執行檔案為當前檔案時,__module__ = __main__ #輸出結果:__main__ print(f.__class__) #輸出當前操作的類名是什麼 #輸出結果:<class ‘__main__.Foo‘>
例子2
檔案結構
├── index.py #執行檔案
│ ├── test
│ │ ├── a.py
a.py內容:
class Bar: def __init__(self): self.name = "nicholas"
index.py內容:
from test.a import Bar ? class Foo: ? def __init__(self,name): self.name = name ? b = Bar() print(b.name) #輸出結果 nicholas print(b.__module__) #輸出結果 test.a ,表示當前操作的類在test.a下 print(b.__class__) #表示當前操作的類名是<class ‘test.a.Bar‘>
七、
__del__
析構方法
析構方法,當對象在記憶體中被釋放時,自動觸發執行。即清除釋放記憶體的方法。
這個是在回收執行個體化對象時觸發執行的方法,刪除對象的屬性不會觸發__del__
方法
註:此方法一般無須定義,因為Python是一門進階語言,程式員在使用時無需關心記憶體的分配和釋放,因為此工作都是交給Python解譯器來執行,所以,解構函式的調用是由解譯器在進行記憶體回收時自動觸發執行的。
例子1
class Foo: def __init__(self,name): self.name = name ? def __del__(self): print("我執行啦!") ? f = Foo("nick") print("-----1") del f.name print("-----2") #這裡是先執行print("-----1")然後執行print("-----2") , # 執行del f.name只是刪除對象字典的"name"屬性,並不會觸發__del__方法
輸出結果
-----1 -----2 我執行啦!
例子2
class Foo: def __init__(self,name): self.name = name ? def __del__(self): print("我執行啦!") ? f = Foo("nick") print("-----1") del f #刪除對象才能調用__del__,這裡既執行了__del__方法,又刪除了對象 print("-----2")
輸出結果
-----1 我執行啦! -----2
例子3
class Foo: def __init__(self,name): self.name = name ? def __del__(self): print("我執行啦!") ? f = Foo("nick") import time time.sleep(2)
輸出結果
我執行啦!
分析:這裡在程式結束時,python解譯器自動回收記憶體,執行了__del__
方法
典型的應用情境:
建立資料庫類,用該類執行個體化出資料庫連結化物件,對象本身是存放於使用者空間記憶體中,而連結則是由作業系統管理的,存放於核心空間記憶體中
當程式結束時,python只會回收自己的記憶體空間,即使用者態記憶體,而作業系統的資源則沒有被回收,這就需要我們定製__del__
,在對象被刪除前向作業系統發起關閉資料庫連結的系統調用,回收資源。
例子
class Foo: ? def __del__(self): print("我執行了") ? f = Foo() f.file = open("test.txt") #開啟檔案,在作業系統中開啟了一個檔案, # 拿到了檔案操作符存在了記憶體中 del f.file #刪除了記憶體中的檔案操作符,但是並未關閉作業系統中的檔案, # 這時需要在__del__中加入定製f.file.close(),關閉作業系統的檔案 print("-----1") #最後程式結束python解譯器自動執行記憶體回收,執行了__del__方法
輸出結果
-----1 我執行了
Python之路(第二十六篇) 物件導向進階:內建方法