首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

python装饰器的使用

程序员大咖

点击右侧关注,免费进阶高级!

作者丨lethe_zh

https://www.jianshu.com/p/d307e8ac9e2a

1.装饰者模式

装饰者模式是常用的软件设计模式之一。通过此设计模式,我们能够在不修改任何底层代码情况下,给已有对象赋予新的职责。python中可以用装饰器简单地实现装饰者模式。

1.1 将函数作为参数传递

1.1将函数作为参数传递

在C/C++中,函数指针可以将函数作为参数传递给另一函数。而在python中,函数也是对象的一种,函数可以被引用,也可直接作为参数传入函数,以及作为容器对象的元素。python中可以采用如下方法实现装饰者模式:

#!/usr/bin/env python3.6

# -*- coding: utf-8 -*-

defadd(x, y):

result = x+y

returnresult

deflog(func):

defwrapper(*args, **kwargs):

result = func(*args)

print(func.__name__,'has been called\n')

returnresult

returnwrapper

if__name__ =='__main__':

print(log(add)(1,2))

上述代码中,函数以需要被装饰的函数作为参数,并返回函数对象。被返回的函数的参数为可变参数与(参数会被封装成,参数则会被封装成字典对象),以适应不同函数的不同参数,保证通用性。

1.2装饰器

上面的实现方法有些繁杂,所有调用被装饰的函数之处的代码,都要进行相应修改,自然不符合python简洁易读的特性。因此python中给出相应语法糖来增加可读性和易用性,那便是“装饰器”。

#!/usr/bin/env python3.6

# -*- coding: utf-8 -*-

fromfunctoolsimportwraps

deflog(func):

#@wraps(func)

defwrapper(*args, **kwargs):

result = func(*args)

print(func.__name__,'has been called')

returnresult

returnwrapper

#等价于add = log(add)

@log

defadd(x, y):

result = x+y

returnresult

if__name__ =='__main__':

print(add(1,2))

print(add.__name__)

运行情况如下:

>>print(add(1,2))

add has been called

3

>>print(add.__name__)

wrapper

但上述方法亦有缺陷,原函数add的元数据(比如名字、文档字符串、注解和参数签名)会丢失。为避免缺陷,任何时候你定义装饰器的时候,都应该使用functools库中的@wraps装饰器来注解底层包装函数(代码中注释部分)。@wraps有一个重要特征是它能让你通过属性 __wrapped__ 直接访问被包装函数。

改进后运行情况:

>>print(add(1,2))

addhas been called

3

>>print(add.__name__)

add

1.3解除装饰器

当装饰器已经作用于某函数,而你想撤销它,那么可以访问 __wrapped__属性来访问原始函数

orig_add = add.__wrapped__

orig_add(1,2)

但若使用了多个装饰器, __wrapped__属性会变得不可控,应尽量避免使用。

若有如下代码:

#!/usr/bin/env python3.6

# -*- coding: utf-8 -*-

importfunctools

importtime

defmetric(func):

@functools.wraps(func)

defwrapper(*args,**kv):

print('Decorator1')

f = func(*args,**kv)

returnf

returnwrapper

deflogging(func):

@functools.wraps(func)

defwrapper(*args,**kv):

print('Decorator2')

f = func(*args,**kv)

returnf

returnwrapper

@metric

@logging

defnormalize(name):

sName = name[:1].upper() + name[1:].lower()

print(sName)

if__name__ =='__main__':

normalize('heLlO')

normalize.__wrapper__('')

运行情况如下:

>>normalize('helLo')

Decorator1

Decorator2

Hello

>>normalize.__wrapped__('world')

Decorator2

World

1.4定义带参数的装饰器

fromfunctoolsimportwraps

deflog(text):

defdecorator(func):

@wraps(func)

defwrappering(*args,**kv):

print('%s %s():'%(text,func.__name__))

returnfunc(*args,**kv)

returnwrappering

returndecorator

@log('run')

defnormalize(name):

sName = name[:1].upper() + name[1:].lower()

print(sName)

装饰器函数可以带参数,最外层的函数会将参数传给内层的装饰器函数,即wrappering函数是可以使用log的传入参数的。

装饰器处理过程与下面是等价的:

normalize= log('run')(normalize)

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181011B1SEWW00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券