1. __call__() 方法
對象+() 可以直接調用__call__()方法 , 類似普通函數的調用
class CallTest(object): def __init__(self): print('I am __init__') def __call__(self): print('I am __call__') return True def run(self): print('I am run') return True obj = CallTest() obj.run() # 調用普通方法 對象.func_name() obj() # 調用__call__()方法, 直接 對象()
打印結果:
I am __init__
I am run
I am __call__
可以看到,obj這個對象被實例化出來,如果要調用__call__方法的話,直接obj(),即可調用並返回結果。obj就類似一個函數地址,obj()即執行這個函數。
2. __init__() 方法
構造函數,在生成對象時調用
===========================
__getattr__, __setattr__, __delattr__
1. 調用對象的一個不存在的屬性時會觸發__getattr__方法
2. 刪除對象的一個屬性的時候會觸發__delattr__方法
3. 設置對象(增加/修改)屬性會觸發__setattr__方法
設置對象屬性和刪除對象屬性會觸發__setattr__ 和 __delattr__ 方法,但要注意的是,在調用這兩個方法時,方法內部必須操作類的屬性字典,否則會造成無限遞歸
3. __getattr__() 方法
----調用(獲取)對象屬性
class Foo: a = 1 def __getattr__(self, item): print('run __getattr__') f = Foo() print(f.a) # 屬性存在,就不會觸發__getattr__()方法 # >> 輸出: 1 print(f.b) # 只有在使用點調用屬性且屬性不存在的時候才會觸發,並且返回None # >> 輸出: run __getattr__ # >> 輸出: None
4. __delattr__() 方法
----刪除對象屬性
class Foo: def __delattr__(self, item): print('run __delattr__') # del self.item # 這樣會造成無限遞歸 self.__dict__.pop(item) f = Foo() f.a = 3 print(f.__dict__) # >> 輸出: {'a': 3} print(f.a) # >> 輸出: 3 del f.a # >> 輸出: run __delattr__ print(f.a) # >> 報錯: AttributeError: 'Foo' object has no attribute 'a'
5. __setattr__() 方法
----設置屬性: 增加對象屬性, 修改對象屬性
class Foo: a = 1 def __setattr__(self, key, value): print("run __setattr__") f = Foo() # 沒有賦值,什么都不會發生 f.c = 200 # 如果增加類屬性, 觸發觸發__setattr__()方法 # >> 輸出: run__setattr__ f.a = 2 # 如果修改類屬性, 觸發觸發__setattr__()方法 # >> 輸出: run __setattr__
實例化對象傳參,會觸發__getattr__方法
class Foo: a = 1 def __init__(self, b): self.b = b # 賦值屬性操作 def __setattr__(self, key, value): print("run __setattr__") f = Foo(100) # 如果實例化的時候傳入參數進行賦值屬性操作, 觸發__setattr__()方法 # >> 輸出: run __setattr__
設置屬性時, 方法內部必須操作類的屬性字典
class Foo: a = 1 def __setattr__(self, key, value): # self.key = value # 增加/修改類屬性,會觸發__setattr__()方法,如果這個操作在setattr方法內部,會造成無限遞歸 self.__dict__[key] = value # 使用這種方法會完成增加/修改類屬性的操作 print("run __setattr__") f = Foo() f.y = 3 # 增加/修改類屬性,調用__setattr__()方法 # >> 輸出: run __setattr__ print(f.__dict__) # >> 輸出: {'y': 3}
當我們重寫__setattr__()方法后,方法內部如果不進行屬性字典的操作,那么除非直接操作屬性字典,否則永遠無法完成賦值
class Foo: def __setattr__(self, key, value): print("run __setattr__") f = Foo() f.y = 3 # 設置對象屬性,調用__setattr__()方法,而__setattr__()方法什么都沒干,所以完成不了對象的設置屬性操作 # >> 輸出: run __setattr__ print(f.__dict__) # >> 輸出: {} print(f.y) # 完成不了賦值 # >> 報錯: AttributeError: 'Foo' object has no attribute 'y'
理解了__setattr__()方法的原理,我們就可以利用 __setattr__()方法 實現我們自定義的功能
class Foo: a = 1 dic = {} # 自定義一個空字典 def __setattr__(self, key, value): self.dic[key] = value print("run __setattr__") f = Foo() f.y = 3 # >> 輸出: run __setattr__ print(f.dic) # 給類變量dic添加鍵值對 # >> 輸出: {'y': 3} print(f.__dict__) # 類屬性不發生變化 # >> 輸出: {}
一個小示例:
class Foo: def __init__(self, dic): self._dic = dic def __getattr__(self, item): val = self._dic[item] if isinstance(val, dict): a = Foo(val) return a # 重點: 又返回一個對象 else: return val dic = {'k1': 'v1', 'k2': 'v2'} dic = Foo(dic) print(dic.k1) # >>輸出: v1 print(dic.k2) # >>輸出: v2 dic = {'k1': {'k2': 'v2'}} dic = Foo(dic) print(dic.k1) # >>輸出: 一個對象 <__main__.Foo object at 0x00000000024D7F98> print(dic.k1.k2) # 對象可以繼續點(.)取屬性操作 # >>輸出: v2
原理:
Foo(dic)實例化一個對象, dic.k1觸發__getattr__()方法, val={'k2': 'v2'},當val值為一個字典對象時,if條件成立, 返回一個以val字典為參數的對象,就是說: dic.k1 == Foo({'k2': 'v2'}),這個對象可以繼續通過點(.)調用對象的屬性,如果有多層嵌套,一直循環下去
接着上面的例子繼續:
def v2(arg): return arg dic = {'k1': {'k2': v2}} dic = Foo(dic) ret = dic.k1.k2(100) print(ret) # >> 輸出: 100
6. __getattribute__() 方法
長得和__getattr__那么像,那么__getattribute__與之有什么關系呢?
class Foo: a = 1 def __init__(self, x): self.x = x def __getattribute__(self, item): print('不管屬性[%s]是否存在,我都會執行' % item) f = Foo(100) print(f.a) # >>輸出: 不管屬性[a]是否存在,我都會執行 # >>輸出: None print(f.b) # >>輸出: 不管屬性[b]是否存在,我都會執行 # >>輸出: None print(f.x) # >>輸出: 不管屬性[x]是否存在,我都會執行 # >>輸出: None
當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程中拋出異常AttributeError
class Foo: def __getattr__(self, item): print('run __getattr__') def __getattribute__(self, item): print('不管屬性[%s]是否存在,我都會執行' % item) # raise AttributeError('啦啦啦啦') f = Foo() # print(f.a) # >>輸出: 不管屬性[a]是否存在,我都會執行 # >>輸出: None print(f.a) # 打開注釋,手動拋錯: raise AttributeError('q') # >>輸出: 不管屬性[a]是否存在,我都會執行 # >>輸出: run __getattr__ # >>輸出: None
7. super()
super 的工作原理如下:
def super(cls, inst): mro = inst.__class__.mro() return mro[mro.index(cls) + 1]
其中 cls 代表類, inst 代表實例, super 函數做了兩件事:
1. 獲取實例對象 inst 的類的 MRO 列表
2. 查找 cls 在當前 MRO 列表中的 index ,並返回它的下一個類,即 mro[index + 1]
當使用 super(cls, inst) 時, Python 會在 inst 的 MRO 列表上搜索 cls 的下一個類. 可以看出, 事實上 super 函數和父類沒有實質性的關聯.