python装饰器怎么设置(深入了解和应用Python 装饰器 @decorator)
python装饰器怎么设置
深入了解和应用Python 装饰器 @decorator
python的装饰器(decorator)是一个很棒的机制,也是熟练运用python的必杀技之一。装饰器,顾名思义,就是用来装饰的,它装饰的是一个函数,保持被装饰函数的原有功能,再装饰上(添油加醋)一些其它功能,并返回带有新增功能的函数对象,所以装饰器本质上是一个返回函数对象的函数(确切的说,装饰器应该是可调用对象,除了函数,类也可以作为装饰器)。
在编程过程中,我们经常遇到这样的场景:登录校验,权限校验,日志记录等,这些功能代码在各个环节都可能需要,但又十分雷同,通过装饰器来抽象、剥离这部分代码可以很好解决这类场景。
装饰器是什么?
要理解python的装饰器,首先我们先理解一下python的函数对象。我们知道,在python里一切都是对象,函数也不例外,函数是第一类对象(first-class objects),它可以赋值给变量,也可以作为list的元素,还可以作为参数传递给其它函数。
函数可以被变量引用
定义一个简单的函数:
|
def say_hi(): print ( 'hi!' ) say_hi() # output: hi! |
个变量say_hi2来引用say_hi函数:
|
say_hi2 = say_hi print (say_hi2) # output: <function say_hi at 0x7fed671c4378> say_hi2() # output: hi! |
上面的语句中say_hi2 和 say_hi 指向了同样的函数定义,二者的执行结果也相同。
函数可以作为参数传递给其它函数
|
def say_more(say_hi_func): print ( 'more' ) say_hi_func() say_more(say_hi) # output: # more # hi |
在上面的例子中,我们把say_hi函数当做参数传递给say_more函数,say_hi 被变量 say_hi_func 引用。
函数可以定义在其它函数内部
|
def say_hi(): print ( 'hi!' ) def say_name(): print ( 'tom' ) say_name() say_hi() # output: # hi! # tom say_name() # 报错 |
上述代码中,我们在say_hi()函数内部定义了另外一个函数say_name()。say_name()只在say_hi函数内部可见(即,它的作用域在say_hi函数内部),在say_hi外包调用时就会出错。
函数可以返回其它函数的引用
|
def say_hi(): print ( 'hi!' ) def say_name(): print ( 'tom' ) return say_name say_name_func = say_hi() # 打印hi!,并返回say_name函数对象 # 并赋值给say_name_func say_name_func() # 打印 tom |
上面的例子,say_hi函数返回了其内部定义的函数say_name的引用。这样在say_hi函数外部也可以使用say_name函数了。
前面我们理解了函数,这有助于我们接下来弄明白装饰器。
装饰器(decorator)
装饰器是可调用对象(callable objects),它用来修改函数或类。
可调用对象就是可以接受某些参数并返回某些对象的对象。python里的函数和类都是可调用对象。
函数装饰器,就是接受函数作为参数,并对函数参数做一些包装,然后返回增加了包装的函数,即生成了一个新函数。
让我们看看下面这个例子:
|
def decorator_func(some_func): # define another wrapper function which modifies some_func def wrapper_func(): print ( "wrapper function started" ) some_func() print ( "wrapper function ended" ) return wrapper_func # wrapper function add something to the passed function and decorator returns the wrapper function def say_hello(): print ( "hello" ) say_hello = decorator_func(say_hello) say_hello() # output: # wrapper function started # hello # wrapper function ended |
上面例子中,decorator_func 就是定义的装饰器函数,它接受some_func作为参数。它定义了一个wrapper_func函数,该函数调用了some_func但也增加了一些自己的代码。
上面代码中使用装饰器的方法看起来有点复杂,其实真正的装饰器的python语法是这样的:
装饰器的python语法
|
@decorator_func def say_hi(): print 'hi!' |
@ 符合是装饰器的语法糖,在定义函数say_hi时使用,避免了再一次的赋值语句。
上面的语句等同于:
|
def say_hi(): print 'hi!' say_hi = decorator_func(say_hi) |
装饰器的顺序
|
@a @b @c def foo(): print ( 'foo' ) # 等同于: foo = a(b(c(foo))) |
带参数函数的装饰器
|
def decorator_func(some_func): def wrapper_func( * args, * * kwargs): print ( "wrapper function started" ) some_func( * args, * * kwargs) print ( "wrapper function ended" ) return wrapper_func @decorator_func def say_hi(name): print ( "hi!" + name) |
上面代码中,say_hi函数带有一个参数。通常情况下,不同功能的函数可以有不同类别、不同数量的参数,在写wrapper_func的时候,我们不确定参数的名称和数量,可以通过*args 和 **kwargs 来引用函数参数。
带参数的装饰器
不仅被装饰的函数可以带参数,装饰器本身也可以带参数。参考下面的例子:
|
def use_logging(level): def decorator(func): def wrapper( * args, * * kwargs): if level = = "warn" : logging.warn( "%s is running" % func.__name__) return func( * args) return wrapper return decorator @use_logging (level = "warn" ) def foo(name = 'foo' ): print ( "i am %s" % name) |
简单来说,带参数的装饰器就是在没有参数的装饰器外面再嵌套一个参数的函数,该函数返回那个无参数装饰器即可。
类作为装饰器
前面我们提到装饰器是可调用对象。在python里面,除了函数,类也是可调用对象。使用类装饰器,优点是灵活性大,高内聚,封装性。通过实现类内部的__call__方法,当使用 @ 语法糖把装饰器附加到函数上时,就会调用此方法。
|
class foo( object ): def __init__( self , func): self ._func = func def __call__( self ): print ( 'class decorator runing' ) self ._func() print ( 'class decorator ending' ) @foo def say_hi(): print ( 'hi!' ) say_hi() # output: # class decorator running # hi! # class decorator ending |
functools.wraps
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看看下面例子:
|
def decorator_func(some_func): def wrapper_func( * args, * * kwargs): print ( "wrapper function started" ) some_func( * args, * * kwargs) print ( "wrapper function ended" ) return wrapper_func @decorator_func def say_hi(name): '''say hi to somebody''' print ( "hi!" + name) print (say_hi.__name__) # output: wrapper_func print (say_hi.__doc__) # output: none |
可以看到,say_hi函数被wrapper_func函数取代,它的__name__ 和 docstring 也自然是wrapper_func函数的了。
不过不用担心,python有functools.wraps,wraps本身也是一个装饰器,它的作用就是把原函数的元信息拷贝到装饰器函数中,使得装饰器函数也有和原函数一样的元信息。
|
from functools import wraps def decorator_func(some_func): @wraps (func) def wrapper_func( * args, * * kwargs): print ( "wrapper function started" ) some_func( * args, * * kwargs) print ( "wrapper function ended" ) return wrapper_func @decorator_func def say_hi(name): '''say hi to somebody''' print ( "hi!" + name) print (say_hi.__name__) # output: say_hi print (say_hi.__doc__) # output: say hi to somebody |
类的内置装饰器
- 类属性@property
- 静态方法@staticmethod
- 类方法@classmethod
通常,我们需要先实例化一个类的对象,再调用其方法。
若类的方法使用了@staticmethod或@classmethod,就可以不需要实例化,直接类名.方法名()来调用。
从使用上来看,@staticmethod不需要指代自身对象的self或指代自身类的cls参数,就跟使用普通函数一样。
@classmethod不需要self参数,但第一个参数必须是指代自身类的cls参数。如果在@staticmethod中要调用到这个类的一些属性方法,只能直接类名.属性名,或类名.方法名的方式。
而@classmethod因为持有cls参数,可以来调用类的属性,类的方法,实例化对象等。
总结
通过认识python的函数,我们逐步弄清了装饰器的来龙去脉。装饰器是代码复用的好工具,在编程过程中可以在适当的场景用多多使用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持开心学习网。
原文链接:https://my.oschina.net/JUANererer/blog/3021215
- python语言支持多态(Python实现多态、协议和鸭子类型的代码详解)
- python设置微信(利用python实现在微信群刷屏的方法)
- python获取股票每天k线(使用PyQtGraph绘制精美的股票行情K线图的示例代码)
- python csv文件读取方法(对python读取zip压缩文件里面的csv数据实例详解)
- python爬虫并保存excel实例(Python实现爬取亚马逊数据并打印出Excel文件操作示例)
- pythonpandas操作拆分excel(Python使用pandas和xlsxwriter读写xlsx文件的方法示例)
- python编辑redis(python使用pipeline批量读写redis的方法)
- python中如何定义带走参数的函数(Python函数定义及传参方式详解4种)
- linux切换python版本(linux安装python修改默认python版本方法)
- 如何用python识别微信内容(Python 实现微信防撤回功能)
- python和mysql实战(由Python编写的MySQL管理工具代码实例)
- python分步式进程计算(python中如何使用分步式进程计算详解)
- python在txt指定行添加文本(Python修改文件往指定行插入内容的实例)
- python中迭代器的作用(Python3.5迭代器与生成器用法实例分析)
- python开发网站github(使用 Python 玩转 GitHub 的贡献板推荐)
- pythontkinter详解(python3.6使用tkinter实现弹跳小球游戏)
- 2020年大众7.5代高尔夫R终结特别版 最后的呐喊(2020年大众7.5代高尔夫R终结特别版)
- 七年前的这部剧有毒,全剧只有女主红到发紫,男主至今无人认识(七年前的这部剧有毒)
- 宋轶除了演过于曼丽,原来还演过一个青楼女子(宋轶除了演过于曼丽)
- 赵丽颖第一部当女主的戏,主角配角个个都是实力演员(赵丽颖第一部当女主的戏)
- 乾隆为何这么喜爱白塔原因是什么(乾隆为何这么喜爱白塔原因是什么)
- 逐渐消失的东北八大怪现象,进步的社会里我们遗失的是什么(逐渐消失的东北八大怪现象)
热门推荐
- 织梦dede怎么获取当前文档的网址(比较详细全面的织梦DEDECMS目录结构说明)
- docker安装nginx如何配置(docker部署nginx并且挂载文件夹和文件操作)
- mysql数据库基本增删改查基本语句(MySQL表的增删改查基础教程)
- sqlserver维护计划保存在哪(SQL Server误设置max server memory的处理方法)
- dockernginx服务器教程(Docker镜像+nginx 部署 vue 项目的方法)
- ubuntu设置用户免密码(Ubuntu修改密码及密码复杂度策略设置方法)
- html元素和属性的区别(HTML5 HTMLCollection和NodeList的区别详解)
- jsonkeyvalue怎么取(替换json对象中的key最佳方案)
- springboot怎么设置docker(Docker如何给Springboot项目动态传参的实现方法)
- sqlserver备份还原数据(通过Windows批处理命令执行SQL Server数据库备份)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9