python上下文管理__exit__,___enter___方法-通透式讲解-附加练习-附加代码注释-源码解读

上下文管理作用:

他能帮助我们,在打开一个资源的同时帮我们把资源清除掉
我们来敲一个小demo来看一下效果

import time

class A:
    def __enter__(self):
        print("enter")
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit")

with A():
    time.sleep(5)
    print("+++++++++")
    time.sleep(5)

返回结果
在这里插入图片描述
现在我们可以知道当进入with语句调用def enter(self),然后执行print("++++++"),出执行语句调用def exit(self, exc_type, exc_val,exc_tb)
这就是上下文管理运用的两个方法 enter,exit
这里要注意:当单独调用A()时上下文管理的两个方法不会起到任何作用,只有使用with后才会产生效果

这里我抛出一个问题,下面代码f == a 返回的是True还是False?

import time

class A:
    def __enter__(self):
        print("enter")
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit")
f = A()
with f as a:
    print(f == a)

在这里插入图片描述
运行代码我们发现返回的是False,a的值到底是什么那?我们分别打印一下f和a

import time

class A:
    def __enter__(self):
        print("enter")   # 他的返回值将成为as子句后面变量的值
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit")
f = A()
with f as a:
    print(f == a)
    print(f)
    print(a)

在这里插入图片描述
通过运行结果我们可以得到一个结论,a的值是上下文管理方法enter的返回值

exit(self, exc_type, exc_val, exc_tb)参数作用

class A:
    def __enter__(self):
        print("enter")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        print("exit")
        
with A() :
    raise KeyError("Wrong")

运行结果:
在这里插入图片描述
总结以下几点内容:
在这里插入图片描述
注意点:如果__exit__方法返回的是一个等效为True的值,就会压制异常

class A:
    def __enter__(self):
        print("enter")

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True   # 等效为True
with A() :
    raise KeyError("Wrong")

在这里插入图片描述
就可以看到没有红色的异常字体显示了
在这里插入图片描述
方法1.装饰器

import datetime
import time
import funtools

def logger(fn):
	@funtools.wraps(fn)   # wrapper = wrpas(fn)(wrapper)
    def wrapper(*args,**kwargs):
        start = datetime.datetime.now()
        ret = fn(*args,**kwargs)
        end = (datetime.datetime.now()-start).total_seconds()
        print(end)
        return ret
    return wrapper
@logger # add = logger(add)
def add(x:int,y:int):
    time.sleep(2)
    return x+y

add(5,4)

方法2.上下文管理

import datetime
import time

class TimeIt:
    def __init__(self,fn,output=lambda fn,delta:print("{}:{}".format(fn.__name__,delta))):
        self.fn = fn
        self.output = output
    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)
        self.output(self.fn,delta)
    def __call__(self, x,y):
        return self.fn(x,y)

def add(x:int,y:int):
    time.sleep(2)
    return x+y
with TimeIt(add) as obj:
    obj(5,6)

在这里插入图片描述
进阶版本:用类实现装饰

import datetime
import time

class TimeIt:
    def __init__(self,fn,output=lambda fn,delta:print("{}:{}".format(fn.__name__,delta))):
        self.fn = fn
        self.output = output
        # 因为这里我们不能使用@wraps所以我们只能把装饰器转换成他的等价式
        wraps(fn)(self)   # @wraps(fn) <==>wrapper=wraps(fn)(wrapper)

    def __call__(self, x,y):
        start = datetime.datetime.now()
        ret = self.fn(x,y)
        delta = (datetime.datetime.now()-start).total_seconds()
        self.output(self.fn,delta)
        return ret
@TimeIt  # add = TimeIt(add)
def add(x:int,y:int):
    time.sleep(2)
    return x+y

add(4,6)

在这里插入图片描述

应用场景

在这里插入图片描述

扩展

了解即可,源代码中经常可见,要看了知道是做什么的
在这里插入图片描述
ctlr+鼠标右键进入源码
在这里插入图片描述
我们可以看到源码中很清楚的给了你一个经典的用法

from contextlib import contextmanager

@contextmanager
def a():
	print("~~~~~~enter")  # 相当于__enter__
	try
    	yield 500   # 相当于__enter__,return的值
    finally
    	print("-----exit")  # 相当于__exit__

with a() as f:
    print(f)

返回结果
在这里插入图片描述

发布了14 篇原创文章 · 获赞 7 · 访问量 1995

猜你喜欢

转载自blog.csdn.net/weixin_45154559/article/details/104713040