python学习笔记之装饰器
高阶函数
一个函数能接受另一个函数作为参数传入,这样的一个函数就是高阶函数。
在python中一切皆对象,函数也不例外,函数是可以赋予给一个变量的,看下边代码:
1 2 3 4
| def fn(x): val = x if x >= 0 else -x return val
|
34
20
在上边的代码中定义了一个函数 fn()
,这个函数接收一个参数,函数名是fn
,fn(-34)
和fn(20)
是函数调用。函数名fn
是什么呢?其实它就是指向能计算绝对值函数的一个变量。
function
12
如上,变量f1
已指向了fn
函数,调用fn()
与调用f1()
是一样的效果。得到的结论是变量是可以指向一个函数。
那函数的参数能否接收一个变量呢?以代码来验证:
23
上边把变量a
指向了-23
,再调用fn()
函数时把变量a
传入,得到了正确的结果,所以函数的参数是能接收一个变量。
总结:在python中,变量可以指向一个函数,函数的参数可以接收一个变量,那么函数的参数也就可以是一个函数。下边以实际代码来验证
1 2
| def num(x,y): return y(x)
|
1 2
| def f(a): return a ** 2
|
16
上边直接调用num()
函数,把指向函数f(a)
的函数名f
,即f
实质也是一个变量,作为一个变量传递给了num(x,y)
函数中的参数y
,这样也得到了正确的答案。所以函数中的参数可以接收一个函数。
总结:高阶函数的实质就是函数的参数能够接收另一个函数。
装饰器
1 2 3
| def deco(fn): print('ha ha ha') return fn
|
1 2
| def myfun(): print('call myfun()')
|
ha ha ha
call myfun
采用python的魔法也可以实现上边的相同效果,如下:
1 2 3
| @deco def myfun(): print('call myfun')
|
ha ha ha
@deco
中的@
是语法糖,表示下边定义的函数将会被@
后的那个函数所修饰,即myfun()
函数会被deco(fn)
这个函数所修饰,实质就是执行了myfun = deco(myfun)
,所以打印出了ha ha ha
,并把原来的myfun()
函数返回回来,下边执行myfun()
时就调用了原来的myfun()
函数,输出了call myfun
call myfun
再对上边的deco函数进行修改:
1 2 3 4 5 6
| def deco(fn): def wrap(): print('ha ha ha') print('call {0} funtion'.format(fn.__name__)) fn() return wrap
|
1 2 3
| @deco def myfun(): print('call myfun')
|
ha ha ha
call myfun funtion
call myfun
myfun()
函数被deco(fn)
函数进行修饰后,调用myfun()
时就像被施加了魔法一样在执行此函数前附加的执行了一些操作,当然也可以在执行函数后附加一些操作。
上边被装饰的myfun()
函数是一个无参的函数,如果被装饰的函数需要接收参数呢?这时装饰器应该这样的来定义:
1 2 3 4 5 6
| def deco(fn): def wrap(*args,**kwargs): print('ha ha ha') print('call {0} funtion'.format(fn.__name__)) return fn(*args,**kwargs) return wrap
|
考虑到被装饰的函数可以接收的参数类型的不确定性,可以用可变位置参数和可变关键字参数来捕捉,即(*args,**kwargs)
1 2 3
| @deco def myfun(x): return x ** 2
|
ha ha ha
call myfun funtion
25
'wrap'
myfun(x)
函数被deco(fn)
函数装饰后,函数对象的__name__
属性会发生改变,发上输出。因为@deco
就相当于执行了myfun = deco(myfun)
,即变量myfun
已经指向了wrap(*args,**kwargs)
函数,这时myfun
变量指向函数的__name__
属性就是wrap
,不再是原来的myfun()
函数的__name__
属性。如果要修正这个问题,可以直接引用python内置的functools.wraps
方法,如下:
1 2 3 4 5 6 7 8
| import functools def deco(fn): @functools.wraps(fn) def wrap(*args,**kwargs): print('ha ha ha') print('call {0} funtion'.format(fn.__name__)) return fn(*args,**kwargs) return wrap
|
1 2 3
| @deco def myfun(x): return x ** 2
|
ha ha ha
call myfun funtion
625
'myfun'
总结:装饰器其实也是一个函数,此函数可以接收一个函数作为参数,并返回一个函数,即也是一个高阶函数。装饰器这个函数能让一个函数在调用时的前或后额外的执行一些操作来修改原调用的函数。