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

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

调用say()应返回:

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

当前回答

当然,您也可以从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()

其他回答

查看文档以了解装饰器是如何工作的。以下是您的要求:

from functools import wraps

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

def makeitalic(fn):
    @wraps(fn)
    def wrapper(*args, **kwargs):
        return "<i>" + fn(*args, **kwargs) + "</i>"
    return wrapper

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

@makebold
@makeitalic
def log(s):
    return s

print hello()        # returns "<b><i>hello world</i></b>"
print hello.__name__ # with functools.wraps() this returns "hello"
print log('hello')   # returns "<b><i>hello</i></b>"

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

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 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 jwt_or_redirect(fn):
  @wraps(fn)
  def decorator(*args, **kwargs):
    ...
    return fn(*args, **kwargs)
  return decorator

def jwt_refresh(fn):
  @wraps(fn)
  def decorator(*args, **kwargs):
    ...
    new_kwargs = {'refreshed_jwt': 'xxxxx-xxxxxx'}
    new_kwargs.update(kwargs)
    return fn(*args, **new_kwargs)
  return decorator

以及最终功能:

@app.route('/')
@jwt_or_redirect
@jwt_refresh
def home_page(*args, **kwargs):
  return kwargs['refreched_jwt']

或者,您可以编写一个工厂函数,该函数返回一个装饰器,该装饰器将装饰函数的返回值包装在传递给工厂函数的标记中。例如:

from functools import wraps

def wrap_in_tag(tag):
    def factory(func):
        @wraps(func)
        def decorator():
            return '<%(tag)s>%(rv)s</%(tag)s>' % (
                {'tag': tag, 'rv': func()})
        return decorator
    return factory

这使您能够编写:

@wrap_in_tag('b')
@wrap_in_tag('i')
def say():
    return 'hello'

or

makebold = wrap_in_tag('b')
makeitalic = wrap_in_tag('i')

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

就我个人而言,我会用不同的方式来编写装饰器:

from functools import wraps

def wrap_in_tag(tag):
    def factory(func):
        @wraps(func)
        def decorator(val):
            return func('<%(tag)s>%(val)s</%(tag)s>' %
                        {'tag': tag, 'val': val})
        return decorator
    return factory

这将产生:

@wrap_in_tag('b')
@wrap_in_tag('i')
def say(val):
    return val
say('hello')

不要忘了decorator语法是一种简写的构造:

say = wrap_in_tag('b')(wrap_in_tag('i')(say)))