装饰器基本概念
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)
一切皆对象
首先我们来理解下 Python 中的函数:
def hello(name='python'):
return 'hello '+name
print(hello())
>>> hello python
# 我们可以将一个函数赋值给一个变量(也就是说函数可以作为参数传递给其他函数),比如
a=hello
# 这里我们并没有使用小括号,并不是在调用函数,而是将它放到变量a里面,我们尝试运行下这个
print(a())
>>> hello python
# 如果我们删掉原来的hello函数,我们依然可以使用a()去调用函数
在函数中定义函数
刚才那些就是函数的基本知识了。我们来让你的知识更进一步。在 Python 中我们可以在一个函数中定义另一个函数
def hello(name='python'):
def how():
print('how are you?')
how()
print('hello '+name)
hello()
>>> how are you?
>>> hello python
# 当我们在调用hello()时,how()也会被调用执行
# 而内部的函数how()在hello()函数之外是不能访问的,如
how()
>>> NameError: name 'how' is not defined
现在我们知道了可以在函数中定义另外的函数。也就是说:我们可以创建嵌套的函数。
从函数中返回函数
其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来
def hello(name='python'):
def how():
print('how are you?')
print('hello '+name)
return how
a=hello()
print(a)
>>> <function how at 0x000001DF1ED672F0>
# 上面清晰的展示了a现在指向how()函数
# a()则会调用how()函数
a()
>>> how are you?
再次看看这个代码。在 return语句中我们返回 how 而不是 how()。为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
将函数作为参数传给另一个函数
在之前我们提到过可以将函数赋值给变量,那我们同样可以将其作为参数传递给另外的函数
def how():
print('how are you?')
def hello(fun,name='python'):
print('hello '+name)
fun()
hello(how)
>>> hello python
>>> how are you?
在这里hello()函数就相当于一个装饰函数
第一个装饰器
def decorator(fun):
def how():
print('hello python')
print('how are you?')
fun()
return how
def func():
print('i am fine, thank you ')
a=decorator(func)
a()
>>> hello python
how are you?
i am fine, thank you
你看明白了吗?我们刚刚应用了之前学习到的原理。这正是 python 中装饰器做的事情!它们封装一个函数,并且用这样或者那样的方式来修改函数的的行为。下面通过使用@来使用装饰器
def decorator(fun):
def how():
print('hello python')
print('how are you?')
fun()
return how
@decorator #当装饰器用来装饰某函数时,被装饰的函数名被自动传参给装饰器函数
def func():
print('i am fine, thank you ')
func()
>>> hello python
how are you?
i am fine, thank you
希望你现在对 Python 装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题
print(func.__name__)
>>> how
这并不是我们想要的!Ouput输出应该是"func"。这里的函数被how替代了。
它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,
那就是functools.wraps。我们修改上一个例子来使用functools.wraps:
from functools import wraps
def decorator(fun):
@wraps(fun)
def how():
print('hello python')
print('how are you?')
fun()
return how
@decorator #当装饰器用来装饰某函数时,被装饰的函数名被自动传参给装饰器函数
def func():
print('i am fine, thank you ')
print(func.__name__)
>>> how
注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。
多个装饰器
一个函数还可以同时定义多个装饰器,比如:
@a
@b
@c
def f ():
pass
它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,
它等效于f = a(b(c(f)))
递归函数装饰
# 装饰递归函数时,要注意返回值
def warp(fn):
def nei(*args):
print('我是装饰器')
a=fn(*args)
print(a)
print('结束')
return a
return nei
@warp
def func1(n):
if n<=2:
return 1
else:
return func1(n-2)+func1(n-1)
func1(5)