我如何在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'

其他回答

Python装饰器为另一个函数添加了额外的功能

斜体装饰符可以如下所示

def makeitalic(fn):
    def newFunc():
        return "<i>" + fn() + "</i>"
    return newFunc

注意,函数是在函数内部定义的。它基本上是用新定义的函数替换函数。例如,我有这门课

class foo:
    def bar(self):
        print "hi"
    def foobar(self):
        print "hi again"

现在,我希望两个函数在完成后和完成前都打印“---”。我可以在每个打印语句前后添加一个打印“---”。但因为我不喜欢重复自己,我会做一个装饰师

def addDashes(fn): # notice it takes a function as an argument
    def newFunction(self): # define a new function
        print "---"
        fn(self) # call the original function
        print "---"
    return newFunction
    # Return the newly defined function - it will "replace" the original

所以现在我可以把我的班级改成

class foo:
    @addDashes
    def bar(self):
        print "hi"

    @addDashes
    def foobar(self):
        print "hi again"

有关装饰器的详细信息,请查看http://www.ibm.com/developerworks/linux/library/l-cpdecor.html

说到计数器示例-如上所述,计数器将在使用decorator的所有函数之间共享:

def counter(func):
    def wrapped(*args, **kws):
        print 'Called #%i' % wrapped.count
        wrapped.count += 1
        return func(*args, **kws)
    wrapped.count = 0
    return wrapped

这样,您的装饰器可以重复用于不同的函数(或用于多次装饰同一个函数:func_counter1=counter(func);func_counter2=counter(func)),并且计数器变量将对每个变量保持私有。

装饰只是语法上的糖。

This

@decorator
def func():
    ...

扩展到

def func():
    ...
func = decorator(func)

用不同数量的参数修饰函数:

def frame_tests(fn):
    def wrapper(*args):
        print "\nStart: %s" %(fn.__name__)
        fn(*args)
        print "End: %s\n" %(fn.__name__)
    return wrapper

@frame_tests
def test_fn1():
    print "This is only a test!"

@frame_tests
def test_fn2(s1):
    print "This is only a test! %s" %(s1)

@frame_tests
def test_fn3(s1, s2):
    print "This is only a test! %s %s" %(s1, s2)

if __name__ == "__main__":
    test_fn1()
    test_fn2('OK!')
    test_fn3('OK!', 'Just a test!')

结果:

Start: test_fn1  
This is only a test!  
End: test_fn1  
  
  
Start: test_fn2  
This is only a test! OK!  
End: test_fn2  
  
  
Start: test_fn3  
This is only a test! OK! Just a test!  
End: test_fn3  

我如何在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'

结论

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

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