我如何在Python中制作两个装饰器来完成以下操作?
@make_bold
@make_italic
def say():
return "Hello"
调用say()应返回:
"<b><i>Hello</i></b>"
我如何在Python中制作两个装饰器来完成以下操作?
@make_bold
@make_italic
def say():
return "Hello"
调用say()应返回:
"<b><i>Hello</i></b>"
当前回答
我如何在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'
结论
所以我们看到,包装使包装函数几乎可以做所有的事情,除了告诉我们该函数将什么作为参数。
还有其他模块可以尝试解决这个问题,但标准库中还没有解决方案。
其他回答
装饰只是语法上的糖。
This
@decorator
def func():
...
扩展到
def func():
...
func = decorator(func)
做同样事情的另一种方式:
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'
查看文档以了解装饰器是如何工作的。以下是您的要求:
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>"
装饰器接受函数定义并创建一个新函数,该函数执行该函数并转换结果。
@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,它在内部执行函数并转换结果。
用不同数量的参数修饰函数:
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