装饰器-打扮函数

一、装饰器 意义在于 高阶函数 传进去一个函数作为参数,然后在装饰器中对这个传进来的函数进行加工打扮之后返回一个函数,返回的函数已经不是原来的函数了

带参数的装饰器

上面的例子,我们增强了函数 hello 的功能,给它的返回加上了标签 <i>...</i>,现在,我们想改用标签 <b>...</b> 或 <p>...</p>。是不是要像前面一样,再定义一个类似 makeitalic 的装饰器呢?其实,我们可以定义一个函数,将标签作为参数,返回一个装饰器,比如:

def wrap_in_tag(tag):
    def decorator(func):
        def wrapped(*args, **kwargs):
            ret = func(*args, **kwargs)
            return '<' + tag + '>' + ret + '</' + tag + '>'
        return wrapped

    return decorator

现在,我们可以根据需要生成想要的装饰器了:

makebold = wrap_in_tag('b')  # 根据 'b' 返回 makebold 生成器

@makebold
def hello(name):
    return 'hello %s' % name

>>> hello('world')
'<b>hello world</b>'

上面的形式也可以写得更加简洁:

@wrap_in_tag('b')
def hello(name):
    return 'hello %s' % name

这就是带参数的装饰器,其实就是在装饰器外面多了一层包装,根据不同的参数返回不同的装饰器。

多个装饰器

现在,让我们来看看多个装饰器的例子,为了简单起见,下面的例子就不使用带参数的装饰器。

def makebold(func):
    def wrapped():
        return '<b>' + func() + '</b>'

    return wrapped

def makeitalic(func):
    def wrapped():
        return '<i>' + func() + '</i>'

    return wrapped

@makebold
@makeitalic
def hello():
    return 'hello world'

上面定义了两个装饰器,对 hello 进行装饰,上面的最后几行代码相当于:

def hello():
    return 'hello world'

hello = makebold(makeitalic(hello))
调用函数 hello:

>>> hello()
'<b><i>hello world</i></b>'

基于类的装饰器

前面的装饰器都是一个函数,其实也可以基于类定义装饰器,看下面的例子:

class Bold(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        return '<b>' + self.func(*args, **kwargs) + '</b>'

@Bold
def hello(name):
    return 'hello %s' % name

>>> hello('world')
'<b>hello world</b>'

可以看到,类 Bold 有两个方法:

  • __init__():它接收一个函数作为参数,也就是被装饰的函数
  • __call__():让类对象可调用,就像函数调用一样,在调用被装饰函数时被调用

还可以让类装饰器带参数:

class Tag(object):
    def __init__(self, tag):
        self.tag = tag

    def __call__(self, func):
        def wrapped(*args, **kwargs):
            return "<{tag}>{res}</{tag}>".format(
                res=func(*args, **kwargs), tag=self.tag
            )
        return wrapped

@Tag('b')
def hello(name):
    return 'hello %s' % name

需要注意的是,如果类装饰器有参数,则 __init__ 接收参数,而 __call__ 接收 func

装饰器的副作用

前面提到,使用装饰器有一个瑕疵,就是被装饰的函数,它的函数名称已经不是原来的名称了,回到最开始的例子:

def makeitalic(func):
    def wrapped():
        return "<i>" + func() + "</i>"
    return wrapped

@makeitalic
def hello():
    return 'hello world'

函数 hello 被 makeitalic 装饰后,它的函数名称已经改变了:

>>> hello.__name__
'wrapped'

为了消除这样的副作用,Python 中的 functools 包提供了一个 wraps 的装饰器:

from functools import wraps

def makeitalic(func):
    @wraps(func)       # 加上 wraps 装饰器
    def wrapped():
        return "<i>" + func() + "</i>"
    return wrapped

@makeitalic
def hello():
    return 'hello world'

>>> hello.__name__
'hello'

转载于:https://www.cnblogs.com/hearecho/p/8675636.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值