据说装饰器采用的是一种切面的思想。即在不改动源码的情况下从切面为函数增加功能。
def myl():
print('this is myl')
这是一个函数,如果我想实现在运行函数之前打印start 之后打印end 要怎么办呢
def myl():
print('start')
print('this is myl')
print('end')
---------------
def do1():
print('start')
def do2():
print('end')
def myl():
do1()
print('this is myl')
do2()
采用装饰器的方式:
一、最简单的装饰器
def de(func):
def w():
print('w')
return func()
return w
@de
def myl():
print('im is x')
myl()
######
w
im is x
######
myl()这个过程:
1、myl
调用myl后先将myl函数传入装饰器de,并且调用了装饰器函数,并返回了装饰器的内部函数
此时的 myl 已经不是原来的myl了,相当于调用了
de(myl)
print(myl) # <function de.<locals>.w at 0x109f74620> 可见已经是w函数了
print(type(myl)) # <class 'function'>
print(myl.__name__) #w
2、myl()
那调用myl() 就相当于调用de(myl)() 即w() ps: w为内部方法,在外部不能调用,这里只是比较
3、w函数被调用了,则执行w中的代码
作为一个装饰器,w除了执行自己的代码外还必须调用传进来的参数,也就是被装饰的函数。也可以不返回函数调用,在内部直接调用,都会一样的。
当然也可以不调用,但这将失去装饰器的意义。
那要实现刚才的功能可以这样写:
def de(func):
def w():
print('start')
func()
print('end')
@de
def myl():
print('this is myl')
myl()
为了证明上面的第一点:
def de(f):
def w():
print('w')
return f
return w() #将这里改成直接调用
@de
def myl():
print('im is myl1')
return 'xxx'
print(myl)
######
w
im is myl1
xxx
######
1的改变可以用functiontools中的装饰器wraps优化
def de(f):
from functools import wraps
@wraps(f)
def w():
print('w')
return f()
return w
@de
def myl():
print('im is myl1')
return 'xxx'
print(myl)
#######
<function myl at 0x10667a620> 看又变回来了,但是没有影响装饰器的使用。
#######
二、我的函数需要传入参数,并且使用装饰器
def de(func):
def w(*args,**kwargs):
print('this is w')
return func(*args,**kwargs)
return w
@de
def myl(x):
print('this is myl {0}'.format(x))
根据上面简单的装饰器可以知道整个过程就是 myl() ---> de(myl)() --> w() --->func()
myl('x') ---> de(myl)('x') ---> w('x') --- > func('x')
所以只要在w函数处接收参数就可以了,为了可以接到任意的参数,使用*args 和 **kwargs
三、给装饰器传递参数
具体方案就是在装饰器的外边加如一层函数,让这层函数返回内部函数
def de1(num):
def de(func):
def w():
print('this is w {num}'.format(num = num))
return func()
return w
return de
@de1(100)
def myl(x):
print('this is myl {0}'.format(x))
个人感觉应该是这样运行的(估计感觉错了) de1(100)(myl)() --- return ---> func()
四、用一个类作为装饰器
主要是用到了__call__ 方法。由于__call__ 方法的作用,使本来不可调用的类变成了可以调用的。
class Loo():
def __init__(self,func):
self.func = func
def __call__(self, *args, **kwargs):
print('this is call {aaa}'.format(aaa = self.func.__name__))
return self.func(*args, **kwargs)
@Loo
def myl(x):
print('this is myl %s'%(x))
myl(100)
########
this is call myl
this is myl 100
########
如果不传参__call__ 方法可以不需要*args 和 **kwargs
五、用一个类作为装饰器并且传参
class Loo1():
def __init__(self,x):
self.x = x
def __call__(self, func):
def w(y):
print('this is w {aaa}-->{bbb}'.format(aaa = func.__name__,bbb = self.x))
return func(y)
return w
@Loo1(100)
def myl2(y):
print('this is myl2 %s'%(y))
a = myl2('x')
########
this is w myl2-->100
this is myl2 x
########