美文网首页
21. 装饰器

21. 装饰器

作者: 牛崽儿酷 | 来源:发表于2016-07-15 10:59 被阅读0次

**装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。
**

先来看一个简单例子:
def foo(): print('i am foo')
现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码:
def foo(): print('i am foo') logging.info("foo is running")

bar()、bar2()也有类似的需求,怎么做?再写一个logging在bar函数里?这样就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志 ,日志处理完之后再执行真正的业务代码
def use_logging(func): logging.warn("%s is running" % func.__name__) func() def bar(): print('i am bar')

use_logging(bar)

逻辑上不难理解,而且运行正常。 但是这样的话,我们每次都要将一个函数作为参数传递给use_logging函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行运行bar(),但是现在不得不改成use_logging(bar)。那么有没有更好的方式的呢?当然有,答案就是装饰器。

简单装饰器

def use_logging(func): def wrapper(*args, **kwargs): logging.warn("%s is running" % func.__name__) return func(*args, **kwargs) return wrapper def bar(): print('i am bar') bar = use_logging(bar) bar()

函数use_logging就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像bar被use_logging装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。

@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作
@use_logging def foo(): print("i am foo") @use_logging def bar(): print("i am bar")

bar()

如上所示,这样我们就可以省去bar = use_logging(bar)这一句了,直接调用bar()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@use_logging,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。
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)
foo()
上面的use_logging是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level="warn")调用的时候,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 bar(): print ('bar')

bar()

functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、name、参数列表,先看例子:
装饰器
def logged(func): def with_logging(*args, **kwargs): print func.__name__ + " was called" return func(*args, **kwargs) return with_logging
函数
@logged def f(x): """does some math""" return x + x * x

@logged等价于:f = logged(f). 不难发现,函数f被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。
print f.__name__ # prints 'with_logging' print f.__doc__ # prints None

这个问题就比较严重的,好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。
from functools import wraps def logged(func): @wraps(func) def with_logging(*args, **kwargs): print func.__name__ + " was called" return func(*args, **kwargs) return with_logging

@logged def f(x): """does some math""" return x + x * x

print f.__name__ # prints 'f' print f.__doc__ # prints 'does some math'

相关文章

  • 21. 装饰器

    **装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是...

  • 装饰器

    """@装饰器- 普通装饰器- 带参数的装饰器- 通用装饰器- 装饰器装饰类- 内置装饰器- 缓存装饰器- 类实现...

  • typescript 五种装饰器

    装饰器类型 装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(fun...

  • python——装饰器详解

    一、装饰器概念 1、装饰器 装饰器:一种返回值也是一个函数的函数,即装饰器。 2、装饰器目的 装饰器的目的:装饰器...

  • 21. 装饰模式

    定义 装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责,就增加对象功能来说,装饰...

  • Python装饰器

    Python装饰器 一、函数装饰器 1.无参装饰器 示例:日志记录装饰器 2.带参装饰器 示例: 二、类装饰器 示例:

  • Python中的装饰器

    Python中的装饰器 不带参数的装饰器 带参数的装饰器 类装饰器 functools.wraps 使用装饰器极大...

  • 装饰器

    装饰器 decorator类装饰器 带参数的装饰器 举例(装饰器函数;装饰器类;有参与无参) https://fo...

  • TypeScript装饰器

    前言 装饰器分类 类装饰器 属性装饰器 方法装饰器 参数装饰器需要在tsconfig.json中启用experim...

  • python之装饰器模版

    装饰器的作用:装饰器即可以装饰函数也可以装饰类。装饰器的原理:函数也是对象 1.定义装饰器 2.使用装饰器假设de...

网友评论

      本文标题:21. 装饰器

      本文链接:https://www.haomeiwen.com/subject/xeuejttx.html