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面向对象程序设计示例小结)
- python3爬虫实例代码(python3通过selenium爬虫获取到dj商品的实例代码)
- python对象创建流程(Python3.5面向对象与继承图文实例详解)
- python成绩判断系统(Python根据成绩分析系统浅析)
- python和java的共同语法(Python和Java的语法对比分析语法简洁上python的确完美胜出)
- linux切换python版本(linux安装python修改默认python版本方法)
- python的pickle用法(Python multiprocess pool模块报错pickling error问题解决方法分析)
- python3有哪些内置模块(Python3.5内置模块之os模块、sys模块、shutil模块用法实例分析)
- pythoncsv格式转换(Python把对应格式的csv文件转换成字典类型存储脚本的方法)
- python语法规则讲解(计算机二级python学习教程2 python语言基本语法元素)
- python多线程超时设置(解决python线程卡死的问题)
- python解析身份证号(python验证身份证信息实例代码)
- python3循环使用教程(Python3.4学习笔记之 idle 清屏扩展插件用法分析)
- python爬取微博登录数据(Python实现爬取马云的微博功能示例)
- python程序怎么打包(Python 实现文件打包、上传与校验的方法)
- java集成钉钉发送消息(Python实现钉钉发送报警消息的方法)
- 美国倒掉数十万加仑牛奶 上热搜第一,这一幕似曾相识(美国倒掉数十万加仑牛奶)
- 深度 倒牛奶 这一幕为何又在美国上演(深度倒牛奶)
- 美国数十万加仑牛奶倒下水道怎么回事 原因曝光令人心痛(美国数十万加仑牛奶倒下水道怎么回事)
- 探索中国神秘文字(探索中国神秘文字)
- 重温《蜗居》 宋思明选中海藻为红颜知己,纯属巧合,与爱无关(宋思明选中海藻为红颜知己)
- 越南旅游攻略(越南旅游攻略自由行)
热门推荐
- python编程开发环境步骤(python开发准备工作之配置虚拟环境非常重要)
- laravel增删改查接口(laravel-admin 实现给grid的列添加行数序号的方法)
- 云服务硬盘挂载有几种方法(腾讯云 阿里云 挂载硬盘方法数据盘)
- 服务器怎么用虚拟内存(windows云服务器提示虚拟内存不足的解决方案)
- docker ip地址分配(docker添加网桥并设置ip地址范围操作)
- 在电脑上面如何使用wampserver(wampserver软件在本地建立多个站点方法教程)
- mvc中Action前HttpPost的作用
- mysql数据表的创建与管理(MySQL数据操作-DML语句的使用)
- dedecms关闭站点(dedecms 会员登录或者退出直接跳转到首页的修改方法)
- javascript检测内容改变了(JavaScript 检测文件的类型的方法)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9