python之装饰器

装饰器的作用就是给已经存在的对象添加额外的功能

基本格式:

def decorator(func):
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

func就是要传入的函数
可以这样使用:

def fun():
    return 'hello'
decorator(fun)

也可以这样使用:

@decorator
def fun():
    return 'hello'

(注意:@在装饰器这里是作为Python语法里面的语法糖写法,用来做修饰。)

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值 也是一个函数对象。 它经常用于有以下场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计。

def decor_a(fun):
    def decor_b():
        print('I am decor_b')
        fun()
        print('I am fun')
    return decor_b

上段代码可以给fun函数执行前后添加两个打印功能。

需要注意的是装饰器给对象添加完功能之后,对象名就变成了装饰器本身的内嵌函数名,比如第一段代码的装饰器内嵌函数为inner那么func对象被decorator装饰之后返回的对象名就是inner,如何解决这种问题呢?

python的内置模块functools里面有个wraps方法可以做到

比如:

def decorator(func):
    @wraps(func)
    def inner(*args, **kwargs):
        return func(*args, **kwargs)
    return inner

在python的flask框架中的路由装饰器@app.route('/index')里面,毋庸置疑肯定是用到的,因为视图函数名不能相同,当然不能个个都是‘inner’。

带参装饰器

我们也看到装饰器wraps也是带参数的,那我们是不是也可以定义带参数的装饰器呢,我们可以使用一个函数来包裹装饰器,调入这个参数。

def logs(logfile='out.log'):
    def logging_decorator(fun):
        @wraps(fun)
        def wrapped_function(*args, **kwargs):
            log_string = fun.__name__ + " Debug"
            print(log_string)
            # 打开logfile,并写入内容
            with open(logfile, 'a') as opened_file:
                opened_file.write(log_string + '\n')
            return fun(*args, **kwargs)
        return wrapped_function
    return logging_decorator

@logs()
def decor_a():
    pass

如果想要继承功能,比如写一个装饰器,我们还想继续多添加一些功能,那么就不需要重复写之前已经有的功能,直接继承过来就行,这个时候可以写类装饰器。

class Logs(object):
    def __init__(self, logfile='out.log'):
        self.logfile = logfile
    def __call__(self, fun):
        @wraps(fun)
        def wrapped_function(*args, **kwargs):
            log_string = fun.__name__ + " was called"
            print(log_string)
            # 打开logfile并写入
            with open(self.logfile, 'a') as opened_file:
                # 现在将日志打到指定的文件
                opened_file.write(log_string + '\n')
            self.notify()
            # 发送一个通知
            return fun(*args, **kwargs)
        return wrapped_function
    def notify(self):
        pass

@Logs()
def decor_a():
    pass

这个实现有一个优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法
现在,我们给 Logs 创建子类,来添加 email 的功能,当然这个功能不在这里详细赘述。

class EmailLogs(Logs):
    ''' 一个logit的实现版本,可以在函数调用时发送email给管理员 '''
    def __init__(self, email='admin@myproject.com', *args, **kwargs):
        self.email = email
        super(EmailLogs, self).__init__(*args, **kwargs)
    def notify(self):
        # 发送一封email到self.email # 这里就不做实现了
        pass

这里我们继续继承并重写notify方法,完成发送邮件的功能。
此时@EmailLogs 将会和 @Logs 产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。

上一篇:函数的不定长参数


下一篇:Python中常用英文单词大全