__new__
方法是真正的類構造方法,用於產生實例化對象(空屬性)。重寫__new__
方法能夠控制對象的產生過程。
__init__
方法是初始化方法,負責對實例化對象進行屬性值初始化,此方法必須返回None,__new__
方法必須返回一個對象。重寫__init__
方法能夠控制對象的初始化過程。python
# 使用new來處理單例模式 class Student: __instance = None def __new__(cls, *args, **kwargs): if not cls.__instance: cls.__instance = object.__new__(cls) return cls.__instance def sleep(self): print('sleeping...') stu1 = Student() stu2 = Student() print(id(stu1), id(stu2)) # 二者輸出相同 print(stu1 is stu2) # True
我的感受,__new__
通常不多用於普通的業務場景,更多的用於元類之中,由於能夠更底層的處理對象的產生過程。而__init__
的使用場景更多。mysql
二者的目的都是爲了顯式的顯示對象的一些必要信息,方便查看和調試。__str__
被print
默認調用,__repr__
被控制檯輸出時默認調用。即,使用__str__
控制用戶展現,使用__repr__
控制調試展現。sql
# 默認全部類繼承object類,object類應該有一個默認的str和repr方法,打印的是對象的來源以及對應的內存地址 class Student: def __init__(self, name, age): self.name = name self.age = age stu = Student('zlw', 26) print(stu) # <__main__.Student object at 0x0000016ED4BABA90> # 自定義str來控制print的顯示內容,str函數必須return一個字符串對象 # 使用repr = str來偷懶控制檯和print的顯示一致 class Student: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f'{self.__class__}, {self.name}, {self.age}' __repr__ = __str__ stu = Student('zlw', 26) print(stu) # <class '__main__.Student'>, zlw, 26
__call__
方法提供給對象能夠被執行的能力,就像函數那樣,而本質上,函數就是對象,函數就是一個擁有__call__
方法的對象。擁有__call__
方法的對象,使用callable
能夠獲得True
的結果,可使用()
執行,執行時,能夠傳入參數,也能夠返回值。因此咱們可使用__call__
方法來實現實例化對象做爲裝飾器:數據庫
# 檢查一個函數的輸入參數個數, 若是調用此函數時提供的參數個數不符合預約義,則沒法調用。 # 單純函數版本裝飾器 def args_num_require(require_num): def outer(func): def inner(*args, **kw): if len(args) != require_num: print('函數參數個數不符合預約義,沒法執行函數') return None return func(*args, **kw) return inner return outer @args_num_require(2) def show(*args): print('show函數成功執行!') show(1) # 函數參數個數不符合預約義,沒法執行函數 show(1,2) # show函數成功執行! show(1,2,3) # 函數參數個數不符合預約義,沒法執行函數 # 檢查一個函數的輸入參數個數, # 若是調用此函數時提供的參數個數不符合預約義,則沒法調用。 # 實例對象版本裝飾器 class Checker: def __init__(self, require_num): self.require_num = require_num def __call__(self, func): self.func = func def inner(*args, **kw): if len(args) != self.require_num: print('函數參數個數不符合預約義,沒法執行函數') return None return self.func(*args, **kw) return inner @Checker(2) def show(*args): print('show函數成功執行!') show(1) # 函數參數個數不符合預約義,沒法執行函數 show(1,2) # show函數成功執行! show(1,2,3) # 函數參數個數不符合預約義,沒法執行函數
__del__
用於當對象的引用計數爲0時自動調用。
__del__
通常出如今兩個地方:一、手工使用del減小對象引用計數至0,被垃圾回收處理時調用。二、程序結束時調用。
__del__
通常用於須要聲明在對象被刪除前須要處理的資源回收操做app
# 手工調用del 能夠將對象引用計數減一,若是減到0,將會觸發垃圾回收 class Student: def __del__(self): print('調用對象的del方法,此方法將會回收此對象內存地址') stu = Student() # 調用對象的__del__方法回收此對象內存地址 del stu print('下面還有程序其餘代碼') class Student: def __del__(self): print('調用對象的del方法,此方法將會回收此對象內存地址') stu = Student() # 程序直接結束,也會調用對象的__del__方法回收地址
這2個方法用於將一個對象模擬成序列。內置類型如列表、元組均可以被迭代,文件對象也能夠被迭代獲取每一行內容。重寫這兩個方法就能夠實現自定義的迭代對象。函數
# 定義一個指定範圍的天然數類,並能夠提供迭代 class Num: def __init__(self, max_num): self.max_num = max_num self.count = 0 def __iter__(self): return self def __next__(self): if self.count < self.max_num: self.count += 1 return self.count else: raise StopIteration('已經到達臨界') num = Num(10) for i in num: print(i) # 循環打印1---10
重寫此係列方法能夠模擬對象成列表或者是字典,便可以使用key-value
的類型。ui
class StudentManager: li = [] dic = {} def add(self, obj): self.li.append(obj) self.dic[obj.name] = obj def __getitem__(self, item): if isinstance(item, int): # 經過下標獲得對象 return self.li[item] elif isinstance(item, slice): # 經過切片獲得一串對象 start = item.start stop = item.stop return [student for student in self.li[start:stop]] elif isinstance(item, str): # 經過名字獲得對象 return self.dic.get(item, None) else: # 給定的key類型錯誤 raise TypeError('你輸入的key類型錯誤!') class Student: manager = StudentManager() def __init__(self, name): self.name = name self.manager.add(self) def __str__(self): return f'學生: {self.name}' __repr__ = __str__ stu1 = Student('小明') stu2 = Student('大白') stu3 = Student('小紅') stu4 = Student('胖虎') # 當作列表使用 print(Student.manager[0]) # 學生: 小明 print(Student.manager[-1]) # 學生: 胖虎 print(Student.manager[1:3]) # [學生: 大白, 學生: 小紅] # 當作字典使用 print(Student.manager['胖虎']) # 學生: 胖虎
當使用obj.x = y
的時候觸發對象的setattr
方法,當del obj.x
的時候觸發對象的delattr
方法。
當嘗試訪問對象的一個不存在的屬性時 obj.noexist
會觸發getattr
方法,getattr
方法是屬性查找中優先級最低的。
能夠重寫這3個方法來控制對象屬性的訪問、設置和刪除。
**特別注意:若是定義了getattr,而沒有任何代碼(即只有pass),則全部不存在的屬性值都是None而不會報錯,可使用super().__getattr__()方法來處理**調試
class Student: def __getattr__(self, item): print('訪問一個不存在的屬性時候觸發') return '不存在' def __setattr__(self, key, value): print('設置一個屬性值的時候觸發') # self.key = value # 這樣會無限循環 self.__dict__[key] = value def __delattr__(self, item): print('刪除一個屬性的時候觸發') if self.__dict__.get(item, None): del self.__dict__[item] stu = Student() stu.name = 'zlw' # 設置一個屬性值的時候觸發 print(stu.noexit) # 訪問一個不存在的屬性時候觸發 , 返回'不存在' del stu.name # 刪除一個屬性的時候觸發
這是一個屬性訪問截斷器,即,在你訪問屬性時,這個方法會把你的訪問行爲截斷,並優先執行此方法中的代碼,此方法應該是屬性查找順序中優先級最高的。
屬性查找順序:實例的getattribute-->實例對象字典-->實例所在類字典-->實例所在類的父類(MRO順序)字典-->實例所在類的getattr-->報錯code
class People: a = 200 class Student(People): a = 100 def __init__(self, a): self.a = a def __getattr__(self, item): print('沒有找到:', item) def __getattribute__(self, item): print('屬性訪問截斷器') if item == 'a': return 1 return super().__getattribute__(item) stu = Student(1) print(stu.a) # 1
這兩個方法的重寫可讓咱們對一個對象使用with
方法來處理工做前的準備,以及工做以後的清掃行爲。對象
class MySQL: def connect(self): print('啓動數據庫鏈接,申請系統資源') def execute(self): print('執行sql命令,操做數據') def finish(self): print('數據庫鏈接關閉,清理系統資源') def __enter__(self): # with的時候觸發,並賦給as變量 self.connect() return self def __exit__(self, exc_type, exc_val, exc_tb): # 離開with語句塊時觸發 self.finish() with MySQL() as mysql: mysql.execute() # 結果: # 啓動數據庫鏈接,申請系統資源 # 執行sql命令,操做數據 # 數據庫鏈接關閉,清理系統資源