装饰器 = 高阶函数 + 嵌套函数
用来装饰其他函数的,即为其他函数添加特定功能的函数,即装饰器本质也是一个函
函数即变量的思想:即函数也可以像变量一样赋值使用,如
def foo(): print('in foo') a = foo # 把函数赋值给一个变量 a() # 调用这个变量,即和调用函数一样的效果
(1)接收一个函数作为形参
(2)返回一个函数(return 函数)
(1)接收函数作为形参 -> 可以不改变被装饰函数的代码的前提下增加功能
def foo():
print('in foo')
# gf 函数接收一个函数作为变量,故 gf 是一个高阶函数
def gf(func):
# func = foo
# 等同于把一个函数赋值给一个变量 func,然后通过变量 func 调用函数
func() # 通过变量调用函数
# 高阶函数除了可以调用 foo 函数,即执行 foo 函数功能之外,可以附加执行一些功能,如打印函数地址
# 这接近于装饰器的功能:在不改变别的函数源码的前提下给其添加附加功能
# 但高阶函数不是装饰器,因为满足条件一:不改变被装饰函数的源码,但不满足条件二:不改变被装饰函数的调用方式
print(func)
gf(foo) # 改变了函数 foo 的调用方式
(2)返回一个函数(return 函数) -> 可以不改变被装饰函数的调用方式
def foo():
print('in foo')
# gf 函数接收一个函数作为变量,故 gf 是一个高阶函数
def gf(func):
# func = foo
# 等同于把一个函数赋值给一个变量 func,然后通过变量 func 调用函数
return func
# 可以不改变 foo 函数的调用方式
foo = gf(foo)
foo()
但是可以看出高阶函数不能同时满足两个条件,所以高阶函数不是真正的装饰器
通过 def 关键字定义在另一个函数中的函数叫嵌套函数,如
def foo():
print('in foo')
def boo():
print('in boo')
装饰器 = 高阶函数 + 嵌套函数,所以可以得到如下代码
import time
def timer(func):
# func = foo
# 接收一个函数,可以在不改变函数源码的情况下为其增加附加功能
def gf():
start_time = time.time()
func() # => foo()
end_time = time.time()
print('func 运行时间为:', end_time - start_time)
# 返回一个函数,可以不改变被嵌套函数的调用方式
return gf
def foo():
time.sleep(2)
print('in foo')
foo = timer(foo)
foo() # 函数调用方式不变,又多了打印其运行时间的附加功能
@的形式是Python 提供的装饰器的语法糖,等价于 foo = timer(foo)
import time
def timer(func):
# func = foo
# 接收一个函数,可以在不改变函数源码的情况下为其增加附加功能
def gf():
start_time = time.time()
func() # => foo()
end_time = time.time()
print('func 运行时间为:', end_time - start_time)
# 返回一个函数,可以不改变被嵌套函数的调用方式
return gf
@timer # => foo = timer(foo)
def foo():
time.sleep(2)
print('in foo')
# foo = timer(foo)
foo() # 函数调用方式不变,又多了打印其运行时间的附加功能
可以通过给上述的每一行代码加上断点,运行 debug 就可以看到装饰器的运行过程
带一个参数
import time
def timer(func):
# func = foo
# 接收一个函数,可以在不改变函数源码的情况下为其增加附加功能
def gf(name):
start_time = time.time()
func(name) # => foo(name)
end_time = time.time()
print('func 运行时间为:', end_time - start_time)
# 返回一个函数,可以不改变被嵌套函数的调用方式
return gf
@timer # => foo = timer(foo) => foo = gf
def foo(name):
time.sleep(2)
print('in foo', name)
# foo = timer(foo) # => foo = gf
foo('python') # => gf('python')
带多个参数
import time
def timer(func):
# func = foo
# 接收一个函数,可以在不改变函数源码的情况下为其增加附加功能
# *args, **kwargs 可以接收任意的参数
def gf(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs) # => foo(*args, **kwargs)
end_time = time.time()
print('func 运行时间为:', end_time - start_time)
# 返回一个函数,可以不改变被嵌套函数的调用方式
return gf
@timer # => foo = timer(foo) => foo = gf
def foo(name, age):
time.sleep(2)
print('in foo', name, age)
# foo = timer(foo) # => foo = gf
foo('python', 18) # => gf('python', 18)
import time
def timer(timer_type):
print(timer_type)
def outer(func):
# func = foo
# *args, **kwargs 可以接收任意的参数
def gf(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs) # => foo(*args, **kwargs)
end_time = time.time()
print('func 运行时间为:', end_time - start_time)
# 返回一个函数,可以不改变被嵌套函数的调用方式
return gf
return outer
# 此时 timer('装饰器参数') 带有小括号,表示已经调用了 timer 函数,那么得到了 timer 函数的返回值 outer
# 所以 @timer('装饰器参数') 已经变成了 @outer
@timer('装饰器参数') # => @outer
def foo(name, age):
time.sleep(2)
print('in foo', name, age)
# foo = timer('装饰器参数')(foo) # => foo = outer(foo) # => foo = gf
foo('python', 18) # => gf('python', 18)
# 提示:把每一行代码打断点调试可以更好的理解装饰器的运行过程
import time
def timer(timer_type):
print(timer_type)
def outer(func):
# func = foo
# *args, **kwargs 可以接收任意的参数
def gf(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs) # => foo(*args, **kwargs)
end_time = time.time()
print('func 运行时间为:', end_time - start_time)
return res
# 返回一个函数,可以不改变被嵌套函数的调用方式
return gf
return outer
# 此时 timer('装饰器参数') 带有小括号,表示已经调用了 timer 函数,那么得到了 timer 函数的返回值 outer
# 所以 @timer('装饰器参数') 已经变成了 @outer
@timer('装饰器参数') # => @outer
def foo(name, age):
time.sleep(2)
print('in foo', name, age)
return 'foo 返回了'
# foo = timer('装饰器参数')(foo) # => foo = outer(foo) # => foo = gf
res = foo('python', 18) # => gf('python', 18)
print(res)
# 提示:把每一行代码打断点调试可以更好的理解装饰器的运行过程