装饰器
含义:装饰器就是把原函数包装一下,对原函数进行加工,添加一些附加功能,装饰器本身就是一个函数,就是将被装饰的函数当作参数传递给装饰器,返回包装后的函数。
装饰器是一个函数, 它需要返回一个新的function.这个新的函数一般不会修改被修饰函数的返回结果.
执行时机
1在模块加载时就会执行修饰器函数
2生成一个个被修饰的新的函数.
缺点:屏蔽了原函数的元信息, 例如__name__, __doc__
. 如果要保留原函数的信息, 可使用标准库中的修饰器functools.wrap
:
补充:
1. “@”,与其说是修饰函数倒不如说是引用、调用它修饰的函数。
换言之,修饰符带的那个函数的入口参数,就是下面的那个整个的函数。有点儿类似JavaScript里面的 function a (function () { ... });
2. 解释器读到这样的修饰之后,先解析@后的内容,直接就把@下一行的函数或者类作为 @ 后边的函数的参数,然后将返回值赋值给下一行修饰的函数对象. 即: 从第一个函数修饰符开始,自下而上做参数传递,实际上是使用约定的函数修饰符达到函数嵌套的目的.
需要注意的:
1. 函数先定义,再修饰它;反之会编译器不认识;
2. 修饰符“@”后面必须是之前定义的某一个函数;
3. 每个函数只能有一个修饰符,大于等于两个则不可以。
4. 不允许和函数定义在同一行
5. 只可以在模块或类定义层内对函数进行修饰,不允许修饰一个类。
1比如:多个decorator
@decorator_one
@decorator_two
def func():
pass
相当于:
func = decorator_one(decorator_two(func))
2.比如:带参数的decorator:
@decorator(arg1, arg2)
def func():
pass
相当于:
func = decorator(arg1,arg2)(func)
这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”
EG1:装饰器修饰装饰器
def funA(a):
print ('funA')
def funB(b):
print ('funB')
def funC(c):
print(c.__name__)
print ('funC')
@funA
@funB
@funC
def funD():
print ('funD')
实行结果:
funD
funC
funB
funA
解析:遇到@解释器读到这样的修饰之后,先解析@后的内容,直接就把@下一行的函数或者类作为 @ 后边的函数的参数,然后将返回值赋值给下一行修饰的函数对象. ,第二行也是@,在去第三行,还是@,去第四行,不是,调用第三行的func方法(此时func方法中的形参c是funD方法的引用,打印的c. __name__就是“fund“,在调用第二行funB,第一行funC
注意:1.代码的执行顺序:距离被装饰的函数最近的装饰器开始执行,依次往上
2.代码的打印顺序是从上往下执行
EG2(用类作为修饰器修饰函数)
class myDecorator(object):
def __init__(self, fn):
print("inside myDecorator.__init__()")
self.fn = fn
def __call__(self):
self.fn()
print("inside myDecorator.__call__()")
@myDecorator
def aFunction():
print("inside aFunction()")
print("Finished decorating aFunction()")
aFunction()
实行结果:
inside myDecorator.__init__()
Finished decorating aFunction()
inside aFunction()
inside myDecorator.__call__()
补充:类中的方法作为装饰器(魔法方法必须使用__call__)
class A():
def __call__(self,fnc):
self.fnc = fnc
return self.play
def play(self):
print("AAAAAA")
self.fnc()
print("BBBBBB")
@A()
def sleep():
print("zzzZZ~~~~")
sleep()
实行结果:
AAAAAA
zzzZZ~~~~
BBBBBB
EG3:
class makeHtmlTagClass(object):
def __init__(self, tag, css_class=""):
self._tag = tag
self._css_class = " class='{0}'".format(css_class) \
if css_class !="" else ""
def __call__(self, fn):
def wrapped(*args, **kwargs):
return "<" + self._tag + self._css_class+">" \
+ fn(*args, **kwargs) + "</" + self._tag + ">"
return wrapped
@makeHtmlTagClass(tag="b", css_class="bold_css")
@makeHtmlTagClass(tag="i", css_class="italic_css")
def hello(name):
return "Hello, {}".format(name)
print (hello("Hao Chen"))
实行结果:
<b class='bold_css'><i class='italic_css'>Hello, Hao Chen</i></b>
上面这段代码中,需要注意这几点:
1)如果decorator有参数的话,__init__() 成员就不能传入fn了,而fn是在__call__的时候传入的。
2)这段代码还展示了 wrapped(*args, **kwargs) 这种方式来传递被decorator函数的参数。(其中:args是一个参数列表,kwargs是参数dict)
EG4:
'''对带参数的函数进行装饰,内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
def deco(func):
def _deco(a, b):
print("before myfunc() called.")
ret = func(a, b)
print(" after myfunc() called. result: %s" % ret)
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a + b
myfunc(1, 2)
myfunc(3, 4)
实行结果:
before myfunc() called.
myfunc(1,2) called.
after myfunc() called. result: 3
before myfunc() called.
myfunc(3,4) called.
after myfunc() called. result: 7
EG5:
'''对参数数量不确定的函数进行装饰,参数用(*args, **kwargs),自动适应变参和命名参数'''
def deco(func):
def _deco(*args, **kwargs):
print("before %s called." % func.__name__)
ret = func(*args, **kwargs)
print(" after %s called. result: %s" % (func.__name__, ret))
return ret
return _deco
@deco
def myfunc(a, b):
print(" myfunc(%s,%s) called." % (a, b))
return a+b
@deco
def myfunc2(a, b, c):
print(" myfunc2(%s,%s,%s) called." % (a, b, c))
return a+b+c
myfunc(1, 2)
myfunc(3, 4)
myfunc2(1, 2, 3)
myfunc2(3, 4, 5)
实行结果:
before myfunc called.
myfunc(1,2) called.
after myfunc called. result: 3
before myfunc called.
myfunc(3,4) called.
after myfunc called. result: 7
before myfunc2 called.
myfunc2(1,2,3) called.
after myfunc2 called. result: 6
before myfunc2 called.
myfunc2(3,4,5) called.
after myfunc2 called. result: 12
用Decorator设置函数的调用参数(三种方法)
第一种,通过 **kwargs,这种方法decorator会在kwargs中注入参数。
def decorate_A(function):
def wrap_function(*args, **kwargs):
kwargs['str'] = 'Hello!'
return function(*args, **kwargs)
return wrap_function
@decorate_A
def print_message_A(*args, **kwargs):
print(kwargs['str'])
print_message_A()
实行结果:
Hello!
第二种,约定好参数,直接修改参数
def decorate_B(function):
def wrap_function(*args, **kwargs):
str = 'Hello!'
return function(str, *args, **kwargs)
return wrap_function
@decorate_B
def print_message_B(str, *args, **kwargs):
print(str)
print_message_B()
实行结果:
Hello!
第三种,通过 *args 注入
def decorate_C(function):
def wrap_function(*args, **kwargs):
str = 'Hello!'
#args.insert(1, str)
args = args +(str,)
return function(*args, **kwargs)
return wrap_function
class Printer:
@decorate_C
def print_message(self, str, *args, **kwargs):
print(str)
p = Printer()
p.print_message()
实行结果:
Hello!
EG:
class Sleep:
# 外层函数
def __init__(self, *args):
# 如果有参数就存入对象方便其他方法使用
self.args = args
# 装饰器函数
def __call__(self, func):
# 将基本函数存入对象
self.func = func
# 返回inner函数
return self.inner
# 装饰器函数
def inner(self):
# 扩展功能1
print('-----start')
# 基本函数功能
self.func()
# 扩展功能2
print('-----end')
@Sleep('la') # @Sleep() -> @对象 # @装饰器函数
def sleep():
print('ZZZzzz...')
sleep()
实行结果:
-----start
ZZZzzz...
-----end
Decorator的副作用(正在补充)
参考文档:
https://blog.csdn.net/scdxmoe/article/details/49637629