chapter9.3、可呼叫物件,上下文管理
可呼叫物件
函式也是物件,函式可以呼叫,物件加上括號,就是物件呼叫自己的__call__方法,函式也是可呼叫物件
def foo(): print(foo.__module__,foo.__name__) foo() #等價於 foo.__call__()
都返回__main__ foo
__call__ 類中定義的方法,使例項可以像函式一樣呼叫
格式化返回座標的點
class Point: def __init__(self,x,y): self.x = x self.y= y def __call__(self, *args, **kwargs): return "<Point {}:{}>".format(self.x,self.y) p = Point(4,2) print(p)##返回例項的物件 print(p())##返回<Point 4:2>,呼叫例項,呼叫了__call__
加法器
class Adder: def __call__(self,*args): ret = 0 for x in args: ret += x self.ret= ret return ret adder=Adder() print(adder(1,2,3)) print(adder.ret)
斐波那契數列的實現
class Fib: def __init__(self): self.items = [0,1,1] def __call__(self,n): if n < 0: raise IndexError('Wrong Index') if n < len(self.items): returnself.items[n] for i in range(n-len(self.items)+1): self.items.append(self.items[-1]+self.items[-2]) return self.items[n] fib = Fib() print(fib(5))
使用類來實現斐波那契數列較為合適,還可以快取,檢索
上下文管理
檔案IO操作可以對檔案物件使用上下文管理,使用with...as語法
對類使用上下文管理:
3.5之前open一個class,返回AttributeError:__exit__
3.6之後返回AttributeError:__enter__
當一個物件同時實現了__enter__和__exit__,就屬於上下文管理物件
定義__enter__,進入物件相關的上下文,如果存在,with後又有as,就會將返回值繫結到as後的變數上
__exit__,退出於此物件相關的上下文,exit里加關閉檔案之類的操作
進出with,開啟調enter,結束調exit,
import time class Point: def __init__(self): print('init') def __enter__(self): print("enter") return 100 def __exit__(self, exc_type, exc_val, exc_tb): print('exit') with Point() as f: print('-'*30) print(f) time.sleep(5) print("_"*30) print('____________end____________')
返回中可以看到執行順序,with後邊的例項先建立,呼叫__init__方法,後with開啟呼叫__enter__,
執行with中的語句,退出with時再呼叫__exit__,而且退出時無論上方如何執行都一定會被with清除佔用的資源。
with可以開啟一個上下文執行環境,在執行前做一些準備工作,執行後做一些收尾工作
with Point() as f: raise Exception('error') print('123')
通過上個實驗,可以看到,即使是異常的丟擲都無法阻止with執行__exit__
import sys with Point() as f: sys.exit(100) print('123') print('outer')
這個例子更極端,直接退出當前直譯器,也不會阻止with執行清理
所以,上下文管理是安全的
with語句
class Point: def __init__(self): print('init') def __enter__(self): print('enter')def __exit__(self, exc_type, exc_val, exc_tb): print('exit') p = Point() with p as f: print(p==f)###返回False,as要求__enter__返回值賦給f print('~~~~~')
以上程式碼缺少__enter__的返回值,所以不等
class Point: def __init__(self): print('init') def __enter__(self): print('enter') return self def __exit__(self, exc_type, exc_val, exc_tb): print('exit') p = Point() with p as f: print(p==f) print('~~~~~')
__enter__方法返回值就是上下文中使用的物件,with語法會把它的返回值賦給as字句的變數
__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(exc_type)##型別,此處返回Exception print(exc_val)##此處返回with中返回的異常值,"what's wrong" print(exc_tb)##返回異常追蹤<traceback object at 地址> print('exit') return True###為等效True都會壓制異常 p = Point() with p as f: raise Exception("what's wrong") print('do sth') print('outer')
enter不可加引數
exit中traceback
exit 的return為等效bool值True時,可以壓制異常,為False時不壓制
資源申請和資源釋放
安全性
裝飾器,類成為裝飾器
inspcct看類的簽名
多個裝飾器的順序會有影響,???留問題了
wrapper複習
應用場景,增強功能類似裝飾器,
資源管理,連結管理’
許可權驗證
contextmanager