Loading...
墨滴

图像处理与MATLAB

2021/09/27  阅读:32  主题:姹紫

Python中的装饰器

Python中的装饰器

0 前言

千言万语抵不过一句话:“Matlab 中可以使用 Python.”

今天修改代码过程中遇到装饰器语法,顺便总结一下以方便以后查看,也分享给大家,希望有帮助吧。

装饰器(Decorator)也称为包装器(Wrapper),也叫装饰者模式(设计模式的一种)。

《设计模式》一书中是这样描述装饰者模式的:

动态地给一个对象添加一些额外的职责,就增加功能来说,装饰者模式相比生成子类更为灵活。

Python 中提供了装饰器的语法糖,使用起来非常方便。下面进行详细介绍。

1 预备

所谓装饰器可以理解为把一个对象通过装饰变成“另一个”对象,这个“另一个“对象还是它本身,期间内部状态会发生改变。一个简单的例子:新房 = 装饰器(毛坯房)。毛坯房经过装饰最终变成了新房,但它还是房子!在毛坯房变成新房的这个过程中,会由装饰器执行一些具体的工作,比如粉刷墙、安装家具等,从外界看上去它是一个被装饰的新房子对象,但实际上仍然是一个房子对象(毛坯房,新房都是房子对象)。

回归到万物皆对象的 Python 中,装饰器的本质就是将一个函数对象作为参数传递给另一个函数返回被装饰函数的对象,即

func = decorator(func)

其中decorator称为装饰器函数,func称为需要被装饰的函数对象。比如我们登录一个系统时首先会验证用户名和密码是否正确,验证通过后会准许登入系统。那么装饰器的职责就是专门负责验证用户名和密码是否正确,一个系统如果被这样的装饰器装饰后,那它就具备了登录验证的额外功能。用代码可表示为:

def decorator(func):

    def _decorator(*args):
        name, password = TrueTrue
        if name and password:
            print('verify successfully')
            func(*args)
            print('login successfully')
        else:
            # Do something
            pass

    return _decorator

需要装饰的函数代码如下:

def func(arg1='Study', arg2='System'):
    print('Welcome ' + arg1 + ' ' + arg2 + '!')

一个调用装饰器的脚本看上去是这样的:

>>> func = decorator(func)
>>> print(func.__name__)
_decorator

>>> func()
verify successfully
Welcome Study System!
login successfully

上面第1行代码表示装饰的整个过程,而产生的函数对象func却指向了decorator的局部函数_decorator,与我们期待的func不一样,它重写了函数的名字和注释文档,幸运的是Python提供了一个简单的函数来解决这个问题,即functools.wraps。另外,语句func = decorator(func)可以由语法糖:@decorator来代替。修改上述代码后为:

from functools import wraps

def decorator(func):
    
    @wraps(func)
    def _decorator(*args):
        name, password = TrueTrue
        if name and password:
            print('verify successfully')
            func(*args)
            print('login successfully')
        else:
            # Do something
            pass

    return _decorator


@decorator
def func(arg1='Study', arg2='System'):
    print('Welcome ' + arg1 + ' ' + arg2 + '!')

    
>>> print(func.__name__)
func

>>> func()
verify successfully
Welcome Study System!
login successfully

现在来看,达到了我们预期的结果。

2 不带和带参数的装饰器

编写自定义装饰器有很多方法,但最简单和最容易理解的方法是编写一个函数,返回封装原始函数调用的一个子函数。

不带参数模式:

from functools import wraps

def mydecorator(func):
    
    @wraps(func)
    def _mydecorator(*args, **kwargs):
        # 在调用实际函数之前做些填充工作
        res = func(*args, **kwargs)
        # 做完某些填充工作之后
        return res
    
    return _mydecorator  # 返回子函数

当装饰器需要传入参数时必须使用第二级封装

带参数模式

from functools import wraps

def mydecorator(arg1, arg2):
    
    def _mydecorator(func):
        @wraps(func)
        def __mydecorator(*args, **kwargs):
            # 在调用实际函数之前做些填充工作
            res = func(*args, **kwargs)
            # 做完某些填充工作之后
            return res
        
        return __mydecorator
    
    return _mydecorator  # 返回子函数

3 使用类实现装饰器

一个实例对象在默认情况下不可调用,只有实现了__call__方法后才能调用。

下面介绍用类实现不带和带参数的装饰器

from functools import wraps


# 无参数形式 (第1种方法) 
class CheckLogin:
    
    def __init__(self, func):
        self.f = func
        
    def __call__(self, *args, **kwargs):
        print('*************')
        print('登录验证')
        self.f(*args, **kwargs)


# 无参数形式 (第2种方法)      
class CheckLogin:
    
    def __call__(self, func):
        @wraps(func)
        def _mydecorator(*args, **kwargs):
            # Do something
            return func(*args, **kwargs)
        
        return _mydecorator
# 注意装饰时需要用@CheckLogin()后边一定要带上括号


# 有参数形式
class CheckLogin:
    
    def __init__(self, arg=None):
        self.arg = arg
    
    def __call__(self, func):
        @wraps(func)
        def _mydecorator(*args, **kwargs):
            # Do something
            return func(*args, **kwargs)
        
        return _mydecorator
# 装饰:
@CheckLogin(arg)

使用类实现不带参数装饰器时,有两种方法,需要注意的是第2种方式需要加上()。

4 结语

总结了 Python 的装饰器用法,函数通过嵌套子函数创建装饰器,而类通过重写__call__方法实现装饰器。在创建带参数的装饰器时,通过类来创建会更优雅和方便。

以上


5 参考文献

https://www.runoob.com/w3cnote/python-func-decorators.html

http://c.biancheng.net/design_pattern/

图像处理与MATLAB

2021/09/27  阅读:32  主题:姹紫

作者介绍

图像处理与MATLAB