一、魔術方法
-
__name__:類、函數、方法等的名字?? ??__module__:類定義所在的模塊名? ? ?__class__:對象或類所屬的類? ? ?__bases__:類的基類的元祖,順序為它們在基類列表中出現的順序? ? ?__doc__:類、函數的文檔字符串,如果沒有定義則為?None? ? ?__mro__:類的?mro,class.mro( )?返回的結果保存在 __mro__?中?? ??__dict__:類或實例的屬性,可寫的字典
-
查看屬性__dir__:返回類或者對象的所有成員名稱列表,dir()?函數就是調用 __dir__(),如果提供?__dir__(),則返回屬性的列表(返回值要求必須是可迭代,且只對實例有影響),否則會盡量從?__dir__?屬性中收集信息dir()?對于不同類型的對象具有不同的行為:?? ??? ?? ? 模塊對象,返回的列表包含模塊的屬性名?? ??? ?? ? 類型或類對象,返回的列表包含類的屬性名,以及它的基類的屬性名?? ??? ?? ? 否則,返回列表包含對象的屬性名、它的類的屬性名和類的基類的屬性名
-
__hash__:內建函數?hash()?調用的返回值,返回一個整數,如果定義這個方法,該類的實例就可?hash__eq__:對應 ==?操作符,判斷兩個對象是否相等,返回?bool?值__hash__?方法只是返回一個?hash?值作為?set?的?key,但是去重,還需要? __eq__?來判斷兩個對象是否相當,如果?hash?值相等,只是?hash?沖突,不能說明兩個對象是相等的。因此,一般來說,提供 __hash__?方法是為了作為?set?或者?dict?的?key,所以去重要同時提供 __eq__?方法列表類不可哈希的原因就是因為源碼中有一句 __hash__ = None,也就是說如果調用 __hash__()?相當于?None(),一定報錯
-
__bool__:內建函數?bool(),或者對象放在邏輯表達式的位置,調用這個函數返回布爾值。沒有定義 __bool__(),就找 __len__() 返回長度,非 0?位就是真。如果 __len__()?也沒有定義,那么所有實例都返回真
-
可視化__repr__:內建函數?repr()?對一個對象獲取字符串表達,調用 __repr__?方法返回字符串表達,如果 __repr__?沒有定義,就直接返回?object ,即顯示內存地址信息__str__:str()?函數、內建函數?format()、print()?函數調用,需要返回對象的字符串表達,如果沒有定義,就去調用 __repr__?方法返回字符串表達,如果 __repr__?沒有定義,就直接返回對象的內存地址信息__bytes__:byte()?函數調用,返回一個對象的?bytes?表達,即返回?bytes?對象class A:????def __init__(self, name, age=18):????????self.name = name????????self.age = age????def __repr__(self):????????return ‘repr: {},{}’.format(self.name, self.age)????def __str__(self):????????return ‘str: {},{}’.format(self.name, self.age)????def __bytes__(self):????????# return ‘repr: {},{}’.format(self.name, self.age)????????import json????????return json.dumps(self.__dict__).encode()a = A(‘tom’)print(a)? –> 調用 __str__print(bytes(a))print([a])??–> 列表使用 __str__,但其內部使用 __repr__print(str(a))print(‘str:a, 1’)??–> 字符串直接輸出沒有引號s = ‘1’print(s)print([‘a’], (s,))??–>? 字符串在基本數據類型內部輸出有引號print({s, ‘a’})
str: tom,18b'{“name”: “tom”, “age”: 18}’[repr: tom,18]str: tom,18str:a, 11[‘a’] (‘1’,){‘1’, ‘a’} -
運算符重載<、<=、==、>、>=、!=? 對應? __lt__、__le__、 __eq__?、__gt__、 __ge__、__ne__+、-、*、/、%、//、**、divmod? 對應? __add__、__sub__、__mul__、__truediv__、__mod__、__floordiv__、__pow__、__divmod__+=、-=、*=、/=、%=、//=、**=? 對應? __iadd__、__isub__、__imul__、__itruediv__、__imod__、__ifloordiv__、__ipow__
-
__lt__、__le__、 __eq__?、__gt__、 __ge__、__ne__ 是比較大小必須實現的方法,但是全部寫完太麻煩,使用 @functools.total_ordering 裝飾器就可以大大簡化代碼,但是要求 __eq__ 必須實現,其它方法實現其一即可from functools import total_ordering@total_orderingclass Person:????def __init__(self, name, age):????????self.name = name????????self.age = age????def __eq__(self, other):????????return self.age == other.age????def __gt__(self, other):????????return self.age > other.age
-
容器相關方法__len__:內建函數 len(),返回對象的長度(>=0 的整數),如果把對象當作容器類型看,就如同 list 或者 dict。bool() 函數調用的時候,如果沒有 __bool__ 方法,則會看 __len__ 方法是否存在,存在返回非 0 為真__iter__:迭代容器時調用,返回一個新的迭代器對象__contains__:in?成員運算符,沒有實現,就調用 __iter__?方法遍歷__getitem__:實現?self[key]?訪問,序列對象,key?接受整數為索引,或者切片。對于?set?和?dict,key?為?hashable。key?不存在引發?KeyError?異常__setitem__:和 __getitem__?的訪問類似,是設置值的方法__missing__:字典或其子類使用 __getitem__()?調用時,key?不存在執行該方法
-
購物車代碼實現:class Cart:????def __init__(self):????????self.items = []????def __len__(self):????????return len(self.items)????def additem(self, item):????????self.items.append(item)????def __iter__(self):????????return iter(self.items)????def __getitem__(self, index):????????return self.items[index]????def __setitem__(self, key, value):????????self.items[key] = value????def __str__(self):????????return str(self.items)????def __add__(self, other):????????self.items.append(other)????????return selfcart = Cart()cart.additem(1)cart.additem(‘abc’)cart.additem(3)print(len(cart))print(bool(cart))for x in cart:????print(x)print(3 in cart)print(2 in cart)print(cart[1])cart[1] = ‘xyz’print(cart)print(cart + 4 + 5 + 6)print(cart.__add__(17).__add__(18))
3True1abc3TrueFalseabc[1, ‘xyz’, 3][1, ‘xyz’, 3, 4, 5, 6][1, ‘xyz’, 3, 4, 5, 6, 17, 18] -
函數即對象,對象 foo 加上 (),就是調用對象的 __call__() 方法?? ??? ?__call__():類中定義一個此方法,實例就可以像函數一樣調用
-
緩存數據,便于檢索的斐波那契數列class Fib:????def __init__(self):????????self.items = [0, 1, 1]????def __call__(self, index):????????return self[index]????def __iter__(self):????????return iter(self.items)????def __len__(self):????????return len(self.items)????def __getitem__(self, index):????????if index <= 0:????????????raise IndexError(‘Wrong Index’)????????if index < len(self.items):????????????return self.items[index]????????for i in range(len(self), index+1):????????????self.items.append(self.items[i-1] + self.items[i-2])????????return self.items[index]????def __str__(self):????????return str(self.items)????__repr__ = __str__f = Fib()print(f(10), len(f))print(f(5), len(f))for x in f:????print(x)print(f[5], f[6])
二、上下文管理
-
當一個對象同時實現了 __enter__() 和 __exit__() 方法,它就屬于上下文管理的對象__enter__:進入與此對象相關的上下文,如果存在該方法, with 語法會把該方法的返回值作為綁定到 as 子句中指定的變量上__exit__:退出與此對象相關的上下文實例調用順序是:先調用 __init__,再調用 __enter__,再執行 with 內部的語句塊,最后調用 __exit__?? ??? ??? ??? ??? ??? ?? ?如果 with 內部語句塊有錯誤,也會調用 __exit__,所以上下文管理是安全的?? ??? ??? ??? ??? ??? ?? ?如果調用 sys.exit(),會退出當前解釋器,但是還是會調用 __exit__
-
__enter__?方法沒有其他參數__exit__(self, exc_type, exc_value, traceback)如果該上下文退出時沒有異常,這三個參數都為?None;如果有異常,exc_type?代表異常類型,exc_value?代表異常的值,traceback?代表異常的追蹤信息但是如果 __exit__?方法返回一個等效?True?的值,則壓制異常,否則,繼續拋出異常class Point:????def __init__(self):????????print(‘init’)????def __enter__(self):????????print(‘enter’)????????return self????def __exit__(self, exc_type, exc_val, exc_tb):????????print(‘type:’, exc_type)????????print(‘val:’, exc_val)????????print(‘tb:’, exc_tb)????????print(‘exit’)????????return “abc”p = Point()with p as f:????raise Exception(‘New Error’)????print(‘do sth’)print(‘outer’)
initentertype: <class ‘Exception’>val: New Errortb: <traceback object at 0x000001A4F146BE08>exitouter -
類既可以用在上下文管理,又可以用作裝飾器import datetimeimport timefrom functools import wrapsclass TimeIt:????def __init__(self, fn):????????self.fn = fn????????wraps(fn)(self)????def __enter__(self):????????self.start = datetime.datetime.now()????????return self????def __exit__(self, exc_type, exc_val, exc_tb):????????delta = (datetime.datetime.now() – self.start).total_seconds()????????print(self.fn.__name__, delta)????def __call__(self, *args, **kwargs):????????self.start = datetime.datetime.now()????????ret = self.fn(*args, **kwargs)????????self.delta = (datetime.datetime.now() – self.start).total_seconds()????????print(self.fn.__name__, self.delta)????????return ret@TimeItdef add(x, y):????“””This is function.”””????time.sleep(2)????return x + yprint(add(10, 5))print(add.__doc__)print(‘====================’)with TimeIt(add) as timeitobj:????print(timeitobj(11, 122))print(TimeIt(add).__doc__)
add 2.00000715This is function.====================add 2.000533133add 2.000533This is function. -
上下文應用場景?? ?? ? 增強功能:在代碼執行的前后增加代碼,以增強其功能,類似裝飾器的功能?? ?? ? 資源管理:打開了資源需要關閉,例如 文件對象、網絡連接、數據庫連接等?? ?? ? 權限驗證:在執行代碼之前,做權限的驗證,在 __enter__ 中處理
-
contextlib.contextmanager:它是一個裝飾器實現上下文管理,裝飾一個函數,而不用像類一樣實現,__enter__ 和 __exit__ 方法要求:必須有?yield,就算增加一個異常,也能保證?exit?的執行?? ??? ?? yield?之前的當做 __enter__?方法執行?? ?? ? ? yield?之后的當做 __exit__?方法執行?? ?? ? ? yield?的值作為 __enter__?的返回值import contextlibimport datetimeimport time@contextlib.contextmanagerdef add(x, y):????start = datetime.datetime.now()????try:????????yield x + y????finally:????????delta = (datetime.datetime.now() – start).total_seconds()????????print(delta)with add(4, 5) as f:????# raise Exception(‘Error’)????time.sleep(2)????print(f)
三、反射
-
運行時,區別于編譯時,指的是程序被加載到內存中執行的時候?? ??反射,reflection,指的是運行時獲取類型定義信息? ? ?在 Python?中,能夠通過一個對象,找出其?type、class、attribute?或者?method?的能力,稱為反射或自省? ? ?具有反射能力的函數有:type()、isinstance()、callable()、dir()、getattr()
-
getattr(object, name[, default]):通過?name?返回?object?的屬性值,當屬性不存在,將使用?default?返回,如果沒有?default,則拋出?AttribteError。?name?必須為字符串setattr(object,?name, value):object?的屬性存在,則覆蓋,不存在,則新增hasattr(object, name):判斷對象是否有這個名字的屬性,name?必須為字符串這種動態增刪屬性的方式是運行時改變類或者實例的方式,但是裝飾器或?Mixin?都是定義時就決定了,因此反射能力具有更大的靈活性
-
通過名稱找對象的方法,命令分發器的代碼實現class Dispatcher:????def __init__(self):????????self._run()????def cmd1(self):????????print(“I’m cmd1”)????def cmd2(self):????????print(“I’m cmd2”)????def _run(self):????????while 1:????????????cmd = input(‘Please input a command:’).strip()????????????if cmd == ‘quit’:????????????????break????????????getattr(self, cmd, lambda : print(‘Unknow Command {}’.format(cmd)))()Dispatcher()
-
__getattr__:一個類的屬性會按照繼承關系找,如果找不到,就會執行 __getattr__? 方法,如果沒有此方法,則會拋出 AttributeError 異常找不到屬性屬性查找順序為:instance.__dict__? –> instance.__class__.__dict__? –>? 繼承的祖先類(直到 object)的 __dict__? –> 找不到? –>? 調用 __getattr__()__setattr__:實例通過 點 設置屬性,如同 self.x = x,就會調用 __setattr__(),屬性要加到 __dict__ 中,就需要自己完成此方法可以攔截對實例屬性的增加、修改操作,如果要設置生效,需要自己操作實例的 __dict__????def __setattr__(self, key, value):????????print(key, value)????????self.__dict__[key] = value__delattr__:可以阻止通過實例刪除屬性的操作,但是通過類依然可以刪除屬性__getattribute__:實例的所有屬性訪問,第一個都會調用此方法,它阻止了屬性的查找,該方法應該返回(計算后的)值或者拋出?AttributeError?異常它的?return?值將作為屬性查找的結果,如果拋出?AttributeError?異常,則會直接調用 __getattr__?方法,因為表示屬性沒有找到為了避免在該方法中無限的遞歸,它的實現應該永遠調用基類的同名方法,以訪問需要的任何屬性,例如?object.__getattribute__(slef, name)
-
總結__getattr__():當通過搜索實例、實例的類及祖先類查不到屬性,就會調用此方法__setattr__():通過?點?訪問實例屬性,進行增加、修改都要調用它__delattr__():當通過實例來刪除屬性時調用此方法__getattribute__:實例所有的屬性調用都從這個方法開始
本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/98377