目录
参考这里,非常nice的文章
引用他的话:
写代码要遵循开放封闭原则,简单来说,已经实现的功能代码内部不允许被修改,但外部可以被扩展。装饰器可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出
# 首先定义装饰器
def decorator(arg):
return func
# 调用装饰器
@decorator
def aim_function()
大致的核心的思想是 将需要装饰的目标作为参数传入到装饰器中,装饰器的返回值赋予被修饰的目标。最终,目标可以通过装饰器进行扩展。
需要注意:在执行代码时会按顺序遇到 @decorator 会执行装饰器中的过程一次,而且目标aim_function()最终会指向装饰器的返回值的内存,所以,一般装饰器的返回值会是一个扩展的在其内部封装的函数。
如果多个装饰器修饰一个目标,则按顺序应为从外到里,扩展的过程应从后往前看,aim_func先被@decorator3扩展,在其基础上被@decorator2包装,最后在其结果上被@decorator1包装然后返回。其实就是按参数的传递顺序,从下往上一次作为参数进行传递。
@decorator1
@decorator2
@decorator3
def aim_func()
具体请参考这里
上述文章写的非常nice。一般的用法,举例
# 类里面
class Object:
def __init__(self,)
def __enter__(self):
return object
def __exit__(self, exc_type, exc_val, exc_tb):
def read(self): # 自定义的方法
with Object() as o: # 随便写的
o.read()
# 或者
with Object():
流程:
1、创建Object()实例化对象,调用__enter__中的方法,将其返回值赋给o2、执行 with 下的语句
3、最后由该实例化对象中的__exit__收尾
因此, __enter__ 和 __exit__ 是必须在 类对象里存在的。?如果没有,则报错,如下:
AttributeError: __exit__
Process finished with exit code 1
__exit__中的可选参数如下:
def __exit__(self, exc_type, exc_val, exc_tb):
# exc_type 异常值类型
# exc_val 异常值
# exc_tb 异常值的回溯栈
正常运行时,它们的值都为 None
class Open:
def __init__(self, filename, mode='r', encoding=None):
self.filename = filename
self.mode = mode
self.encoding = encoding
def __enter__(self):
print('开始执行__enter__')
return "来,小亮,给他整个活🐯"
def read(self):
print('以{}模式打开文件{}'.format(self.mode, self.filename))
def __exit__(self, exc_type, exc_val, exc_tb):
print('最后执行 __exit__')
print('草!走!忽略? ? ? ? ?')
print(exc_type)
print(exc_val)
print(exc_tb)
with Open('1.txt') as f:
print(f)
# 输出
开始执行__enter__
来,小亮,给他整个活🐯
最后执行 __exit__
草!走!忽略? ? ? ? ?
None
None
None
如果出现异常,那么异常语句之后的将不会执行并被跳过,直接执行 __exit__,
class Open:
def __init__(self, filename, mode='r', encoding=None):
self.filename = filename
self.mode = mode
self.encoding = encoding
def __enter__(self):
print('开始执行__enter__')
return "来,小亮,给他整个活🐯"
def read(self):
print('以{}模式打开文件{}'.format(self.mode, self.filename))
def __exit__(self, exc_type, exc_val, exc_tb):
print('最后执行 __exit__')
print('草!走!忽略? ? ? ? ?')
print(exc_type)
print(exc_val)
print(exc_tb)
return True
with Open('1.txt') as f:
print(f)
1/0
print('沈阳等你嗷')
print('我们就烩烩你')
# 输出
开始执行__enter__
来,小亮,给他整个活🐯
最后执行 __exit__
草!走!忽略? ? ? ? ?
<class 'ZeroDivisionError'>
division by zero
<traceback object at 0x00000194AD16A788>
我们就烩烩你
Process finished with exit code 0
可以看到 “沈阳等你嗷” 并没有打印, 而 with 之外的 我们烩烩你 执行了。其中__exit__返回 True 则表示程序不报错正常执行,将异常吞掉继续执行其它语句。
若不设置返回值 为True,程序会报错,并崩溃停止执行
# 不设置 return True 打印
Traceback (most recent call last):
File "***", line 31, in <module>
1/0
ZeroDivisionError: division by zero
开始执行__enter__
来,小亮,给他整个活🐯
最后执行 __exit__
草!走!忽略? ? ? ? ?
<class 'ZeroDivisionError'>
division by zero
<traceback object at 0x0000025ADBFAA788>
Process finished with exit code 1
至于设置了返回值为True,后面的 “沈阳等你嗷” 没有打印,是因为在执行 __exit__ 后with语块流程就结束了。遇到异常 则 在异常处 直接提前 跳入执行 __exit__,下面的语句就被跳过了。
所以,根据这个原理,可以设置备选方案,如果一个方案可能遇到执行不通的状况,则这时可以跳过这个方案,执行其它方案。
这个跟
try:
except:
?很像。
具体的参考上述的 with 参考的文章。那个利用他来做 装饰器。
在代码中遇到的
class Profile(contextlib.ContextDecorator):
# YOLOv5 Profile class. Usage: @Profile() decorator or 'with Profile():' context manager
def __init__(self, t=0.0):
self.t = t
self.cuda = torch.cuda.is_available()
def __enter__(self):
self.start = self.time()
return self
def __exit__(self, type, value, traceback):
self.dt = self.time() - self.start # delta-time
self.t += self.dt # accumulate dt
def time(self):
if self.cuda:
torch.cuda.synchronize()
return time.time()
调用
dt = (Profile(), Profile(), Profile())
with dt[0]:
with dt[1]:
with dt[2]:
计算每一步的时间,累计总的时间。
=========================================================================
那一脚踢出了盛夏~