我如何在Python中制作两个装饰器来完成以下操作?

@make_bold
@make_italic
def say():
   return "Hello"

调用say()应返回:

"<b><i>Hello</i></b>"

当前回答

做同样事情的另一种方式:

class bol(object):
  def __init__(self, f):
    self.f = f
  def __call__(self):
    return "<b>{}</b>".format(self.f())

class ita(object):
  def __init__(self, f):
    self.f = f
  def __call__(self):
    return "<i>{}</i>".format(self.f())

@bol
@ita
def sayhi():
  return 'hi'

或者,更灵活地说:

class sty(object):
  def __init__(self, tag):
    self.tag = tag
  def __call__(self, f):
    def newf():
      return "<{tag}>{res}</{tag}>".format(res=f(), tag=self.tag)
    return newf

@sty('b')
@sty('i')
def sayhi():
  return 'hi'

其他回答

装饰器接受函数定义并创建一个新函数,该函数执行该函数并转换结果。

@deco
def do():
    ...

相当于:

do = deco(do)

例子:

def deco(func):
    def inner(letter):
        return func(letter).upper()  #upper
    return inner

This

@deco
def do(number):
    return chr(number)  # number to letter

相当于这个

def do2(number):
    return chr(number)

do2 = deco(do2)

65<=>“a”

print(do(65))
print(do2(65))
>>> B
>>> B

要理解decorator,需要注意的是,decorator创建了一个新的函数do,它在内部执行函数并转换结果。

当然,您也可以从decorator函数返回lambdas:

def makebold(f): 
    return lambda: "<b>" + f() + "</b>"
def makeitalic(f): 
    return lambda: "<i>" + f() + "</i>"

@makebold
@makeitalic
def say():
    return "Hello"

print say()

考虑下面的修饰符,注意我们将wrapper()函数作为对象返回

def make_bold(func):
    def wrapper():
        return '<b>'+func()+'</b>'
    return wrapper

所以这个

@make_bold
def say():
    return "Hello"

计算结果为

x = make_bold(say)

注意,x不是say(),而是在内部调用say(()的包装器对象。这就是装饰师的工作原理。它总是返回调用实际函数的包装器对象。如果链接此

@make_italic
@make_bold
def say():
    return "Hello"

转换为此

x = make_bold(say)
y = make_italic(x)

以下是完整的代码

def make_italic(func):
    def wrapper():
        return '<i>'+func()+'</i>'
    return wrapper


def make_bold(func):
    def wrapper():
        return '<b>'+func()+'</b>'
    return wrapper


@make_italic
@make_bold
def say():
    return "Hello"


if __name__ == '__main__':
    # x = make_bold(say) When you wrap say with make_bold decorator
    # y = make_italic(x) When you also add make_italic as part of chaining
    # print(y())
    print(say())


上述代码将返回

<i><b>Hello</b></i>

希望这有帮助

我如何在Python中制作两个装饰器来完成以下操作?

调用时需要以下函数:

@马克博尔德@使倾斜def say():return“您好”

要返回:

<b><i> 你好</i></b>

简单的解决方案

为了最简单地做到这一点,请制作返回lambdas(匿名函数)的装饰器,这些函数在函数(闭包)上关闭并调用它:

def makeitalic(fn):
    return lambda: '<i>' + fn() + '</i>'

def makebold(fn):
    return lambda: '<b>' + fn() + '</b>'

现在根据需要使用它们:

@makebold
@makeitalic
def say():
    return 'Hello'

现在:

>>> say()
'<b><i>Hello</i></b>'

简单解决方案的问题

但我们似乎几乎失去了最初的功能。

>>> say
<function <lambda> at 0x4ACFA070>

要找到它,我们需要深入研究每个lambda的闭包,其中一个被另一个所掩埋:

>>> say.__closure__[0].cell_contents
<function <lambda> at 0x4ACFA030>
>>> say.__closure__[0].cell_contents.__closure__[0].cell_contents
<function say at 0x4ACFA730>

因此,如果我们将文档放在这个函数上,或者希望能够修饰包含多个参数的函数,或者我们只是想知道在调试会话中看到的是什么函数,那么我们需要对包装器做更多的工作。

全功能解决方案-克服大多数这些问题

我们在标准库中有来自functools模块的修饰符包装!

from functools import wraps

def makeitalic(fn):
    # must assign/update attributes from wrapped function to wrapper
    # __module__, __name__, __doc__, and __dict__ by default
    @wraps(fn) # explicitly give function whose attributes it is applying
    def wrapped(*args, **kwargs):
        return '<i>' + fn(*args, **kwargs) + '</i>'
    return wrapped

def makebold(fn):
    @wraps(fn)
    def wrapped(*args, **kwargs):
        return '<b>' + fn(*args, **kwargs) + '</b>'
    return wrapped

不幸的是,仍然有一些样板,但这是我们所能做到的最简单的。

在Python3中,默认情况下还会分配__qualiname__和__annotations__。

现在:

@makebold
@makeitalic
def say():
    """This function returns a bolded, italicized 'hello'"""
    return 'Hello'

现在:

>>> say
<function say at 0x14BB8F70>
>>> help(say)
Help on function say in module __main__:

say(*args, **kwargs)
    This function returns a bolded, italicized 'hello'

结论

所以我们看到,包装使包装函数几乎可以做所有的事情,除了告诉我们该函数将什么作为参数。

还有其他模块可以尝试解决这个问题,但标准库中还没有解决方案。

这个答案早就有了答案,但我想我会分享我的Decorator类,这使编写新的Decorator变得简单而紧凑。

from abc import ABCMeta, abstractclassmethod

class Decorator(metaclass=ABCMeta):
    """ Acts as a base class for all decorators """

    def __init__(self):
        self.method = None

    def __call__(self, method):
        self.method = method
        return self.call

    @abstractclassmethod
    def call(self, *args, **kwargs):
        return self.method(*args, **kwargs)

首先,我认为这使装饰器的行为非常清晰,但也使定义新的装饰器变得非常简洁。对于上面列出的示例,您可以将其解为:

class MakeBold(Decorator):
    def call():
        return "<b>" + self.method() + "</b>"

class MakeItalic(Decorator):
    def call():
        return "<i>" + self.method() + "</i>"

@MakeBold()
@MakeItalic()
def say():
   return "Hello"

您也可以使用它来执行更复杂的任务,例如,一个装饰器,它会自动将函数递归地应用于迭代器中的所有参数:

class ApplyRecursive(Decorator):
    def __init__(self, *types):
        super().__init__()
        if not len(types):
            types = (dict, list, tuple, set)
        self._types = types

    def call(self, arg):
        if dict in self._types and isinstance(arg, dict):
            return {key: self.call(value) for key, value in arg.items()}

        if set in self._types and isinstance(arg, set):
            return set(self.call(value) for value in arg)

        if tuple in self._types and isinstance(arg, tuple):
            return tuple(self.call(value) for value in arg)

        if list in self._types and isinstance(arg, list):
            return list(self.call(value) for value in arg)

        return self.method(arg)


@ApplyRecursive(tuple, set, dict)
def double(arg):
    return 2*arg

print(double(1))
print(double({'a': 1, 'b': 2}))
print(double({1, 2, 3}))
print(double((1, 2, 3, 4)))
print(double([1, 2, 3, 4, 5]))

哪些打印:

2
{'a': 2, 'b': 4}
{2, 4, 6}
(2, 4, 6, 8)
[1, 2, 3, 4, 5, 1, 2, 3, 4, 5]

注意,这个示例没有在decorator的实例化中包含列表类型,因此在最终的print语句中,该方法应用于列表本身,而不是列表的元素。