python语言教程:装饰器的使用

2018-10-1210:23:31编程语言入门到精通Comments2,771 views字数 2450阅读模式

1.装饰者模式文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

装饰者模式是常用的软件设计模式之一。通过此设计模式,我们能够在不修改任何底层代码情况下,给已有对象赋予新的职责。python中可以用装饰器简单地实现装饰者模式。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

1.1 将函数作为参数传递文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

1.1将函数作为参数传递文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

在C/C++中,函数指针可以将函数作为参数传递给另一函数。而在python中,函数也是对象的一种,函数可以被引用,也可直接作为参数传入函数,以及作为容器对象的元素。python中可以采用如下方法实现装饰者模式:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

def add(x, y):
result = x+y
return result文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

def log(func):
def wrapper(*args, **kwargs):
result = func(*args)
print(func.__name__,'has been called\n')
return result
return wrapper文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

if __name__ == '__main__':
print(log(add)(1,2))文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

上述代码中,log函数以需要被装饰的函数作为参数,并返回函数对象。被返回的函数的参数为可变参数*args与**kwargs(*args参数会被封装成tuple,**kwargs参数则会被封装成字典对象),以适应不同函数的不同参数,保证通用性。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

1.2装饰器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

上面的实现方法有些繁杂,所有调用被装饰的函数之处的代码,都要进行相应修改,自然不符合python简洁易读的特性。因此python中给出相应语法糖来增加可读性和易用性,那便是“装饰器”。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

from functools import wraps文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

def log(func):
#@wraps(func)
def wrapper(*args, **kwargs):
result = func(*args)
print(func.__name__,'has been called')
return result
return wrapper文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

#等价于add = log(add)
@log
def add(x, y):
result = x+y
return result文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

if __name__ == '__main__':
print(add(1,2))
print(add.__name__)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

运行情况如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

>>print(add(1,2))
add has been called
3
>>print(add.__name__)
wrapper文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

但上述方法亦有缺陷,原函数add的元数据(比如名字、文档字符串、注解和参数签名)会丢失。为避免缺陷,任何时候你定义装饰器的时候,都应该使用functools库中的@wraps装饰器来注解底层包装函数(代码中注释部分)。@wraps有一个重要特征是它能让你通过属性 __wrapped__ 直接访问被包装函数。
改进后运行情况:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

>>print(add(1,2))
add has been called
3
>>print(add.__name__)
add文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

1.3解除装饰器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

当装饰器已经作用于某函数,而你想撤销它,那么可以访问 __wrapped__属性来访问原始函数文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

orig_add = add.__wrapped__
orig_add(1,2)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

但若使用了多个装饰器, __wrapped__属性会变得不可控,应尽量避免使用。
若有如下代码:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

#!/usr/bin/env python3.6
# -*- coding: utf-8 -*-文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

import functools
import time文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

def metric(func):
@functools.wraps(func)
def wrapper(*args,**kv):
print('Decorator1')
f = func(*args,**kv)
return f
return wrapper文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

def logging(func):
@functools.wraps(func)
def wrapper(*args,**kv):
print('Decorator2')
f = func(*args,**kv)
return f
return wrapper文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

@metric
@logging
def normalize(name):
sName = name[0:1].upper() + name[1:].lower()
print(sName)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

if __name__ == '__main__':
normalize('heLlO')
normalize.__wrapper__('')文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

运行情况如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

>>normalize('helLo')
Decorator1
Decorator2
Hello
>>normalize.__wrapped__('world')
Decorator2
World文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

1.4定义带参数的装饰器文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

from functools import wraps文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

def log(text):
def decorator(func):
@wraps(func)
def wrappering(*args,**kv):
print('%s %s():'%(text,func.__name__))
return func(*args,**kv)
return wrappering
return decorator文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

@log('run')
def normalize(name):
sName = name[0:1].upper() + name[1:].lower()
print(sName)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

装饰器函数可以带参数,最外层的函数会将参数传给内层的装饰器函数,即wrappering函数是可以使用log的传入参数的。
装饰器处理过程与下面是等价的:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

normalize = log('run')(normalize)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/ymba/6609.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/ymba/6609.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定