python的装饰器、with、上下文管理器等简单的记录

发布时间:2023年12月21日

目录

装饰器

大致流程

with与上下文管理器

上下文管理器


装饰器

参考这里,非常nice的文章

引用他的话:

写代码要遵循开放封闭原则,简单来说,已经实现的功能代码内部不允许被修改,但外部可以被扩展。装饰器可以在不修改原有代码的情况下,为被装饰的对象增加新的功能或者附加限制条件或者帮助输出

大致流程

# 首先定义装饰器
def decorator(arg):
    
    return func


# 调用装饰器
@decorator
def aim_function()

大致的核心的思想是 将需要装饰的目标作为参数传入到装饰器中,装饰器的返回值赋予被修饰的目标。最终,目标可以通过装饰器进行扩展。

需要注意:在执行代码时会按顺序遇到 @decorator 会执行装饰器中的过程一次,而且目标aim_function()最终会指向装饰器的返回值的内存,所以,一般装饰器的返回值会是一个扩展的在其内部封装的函数

如果多个装饰器修饰一个目标,则按顺序应为从外到里,扩展的过程应从后往前看,aim_func先被@decorator3扩展,在其基础上被@decorator2包装,最后在其结果上被@decorator1包装然后返回。其实就是按参数的传递顺序,从下往上一次作为参数进行传递。

@decorator1
@decorator2
@decorator3
def aim_func()

with与上下文管理器

具体请参考这里

上述文章写的非常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__中的方法,将其返回值赋给o

2、执行 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]:

计算每一步的时间,累计总的时间。

=========================================================================

那一脚踢出了盛夏~

文章来源:https://blog.csdn.net/allrubots/article/details/135114555
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。