1. 程式人生 > >chapter9.3、可呼叫物件,上下文管理

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):
            return
self.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