likes
comments
collection
share

Python的装饰器

作者站长头像
站长
· 阅读数 3

装饰器是什么?在Python中,装饰器是使用'@'语法糖的修饰函数的语句,一般出现在函数定义的上方。装饰器的作用是增强被修饰函数的功能,譬如给函数增加日志功能等。

1、无参装饰器

打开Pycharm,我们先实现一个简单的闭包,并定义一个普通函数print_info:

Python的装饰器

在上面代码中,outer_func中嵌套了inner_func的定义,传递func参数,并返回inner_func引用;inner_func定义中,实现了一行打印,并调用了func。普通函数print_info实现了简单打印,仅作演示用。

通常情况下,我们会使用如下方式来用闭包去管理普通函数:

Python的装饰器

print_info作为参数,调用outer_func得到p,然后p就相当于print_info了,可以正常调用,运行一下:

Python的装饰器

但是这种方式比较繁琐,而且不够优雅,需要写很多闭包管理语句,因此Python提供了'@'语法糖:

Python的装饰器

在被修饰函数上面,使用'@装饰器'修饰被修饰函数,然后直接使用print_info即可:

Python的装饰器

运行结果与不使用'@'语法时一样,但是后者更加简洁和优雅。

实际上,可以简单理解使用'@'语法后的行为:

print_info = outer_func(print_info)

这个时候的print_info被修改了,实际上变成了闭包函数,普通函数可以直接使用。

2、有参装饰器

上节的无参装饰器是最简单的,实际应用中可能会需要使用有参装饰器。举个栗子,软件的日志系统通常会分日志级别,比如"debug"、"info"、"warn"、"error"、"fatal"等,如果我们的需求是,不同函数输出不同的日志级别,这个时候使用无参装饰器就无法完成了,必须使用有参装饰器,将日志级别信息通过装饰器传递过去。

在无参装饰器中,两层嵌套函数即可实现无参装饰器,那要实现有参装饰器需要在两层嵌套函数中再增加一层函数定义,形成三层嵌套函数,具体演示如下:

Python的装饰器

在outger_func定义之外增加了log函数定义,在log函数中,传递日志级别level,并返回outer_func定义。同时,在inner_func中,把level给打印出来了。好了,使用这个三层嵌套函数形成的闭包来修饰下print_info:

Python的装饰器

可以看到装饰器可以传递参数了,运行一下:

Python的装饰器

日志级别被正确传递给闭包了。

同无参装饰器一样,可以简单理解有参装饰器使用'@'语法后的行为:

print_info = log('debug')(print_info)

所以'@'后会紧跟闭包定义中最近外层函数的引用,并自动完成上述转换。

3、基于类的装饰器

前两节中的装饰器都是基于闭包的,事实上,闭包很多特性,面向对象中也是具备的,因此基于类也可以实现装饰器。基于类的装饰器没有闭包实现方便,这里不作深入分析,仅作示例:

Python的装饰器

这里补充下,基于类的装饰器是利用魔术方法__call__,这个方法可以类像函数一样执行,效果如下:

# 拥有__call__方法的类
对象 = 类() # 实例化,调用__init__
对象() # 调用__call__方法

4、结束

实际上,装饰器一般出现在大型项目中,譬如大型框架,小型项目通常可以选用。基于函数(闭包)和基于类均可实现装饰器,推荐闭包模式的装饰器,简单易用。