装饰器 = 高阶函数 + 嵌套函数
用来装饰其他函数的,即为其他函数添加特定功能的函数,即装饰器本质也是一个函
函数即变量的思想:即函数也可以像变量一样赋值使用,如
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)
-
- # 提示:把每一行代码打断点调试可以更好的理解装饰器的运行过程
- # 多个装饰器可以放在一个函数上
- def add(func):
- def inner1():
- print("inner1...")
- x = func()
- return x + 1
-
- return inner1
-
-
- def cube(func):
- def inner2():
- print("inner2...")
- x = func()
- return x * x * x
-
- return inner2
-
-
- def square(func):
- def innner3():
- print("inner3...")
- x = func()
- return x * x
-
- return innner3
-
-
- @add
- @cube
- @square
- def test():
- return 2
-
-
- # 从结果判断执行顺序:square -> cube -> add
- # test 函数传递给 square 函数
- # square 函数中的 innner3 函数传递给 cube 函数
- # cube 函数中的 innner2 传递给 add 函数
- # add 函数中的 innner1 传递给 test
- print(test()) # 65