目录一、装饰器概述,接下来我们就来聊聊关于python装饰器简明教程?以下内容大家不妨参考一二希望能帮到您!

python装饰器简明教程(终于搞明白了Python装饰器)

python装饰器简明教程

目录

一、装饰器概述

二、装饰器的入门

1.什么是闭包

2.什么是装饰器

3.闭包与装饰器的区别

4.为何要用装饰器

5.适用场景

三、如何使用装饰器

1.通过案例了解装饰器的用法

2.Python装饰器执行顺序详解

四、装饰器传参

五、装饰器返回值

六、通用装饰器

七、装饰器带参数


一、装饰器概述

装饰器是一个函数,该函数是用来为其他函数添加额外的功能,就是拓展原来函数功能的一种函数。


二、装饰器的入门1.什么是闭包

要掌握装饰器先得理解闭包,掌握了闭包以后再学装饰器就很容易了。

闭包就是外部函数中定义一个内部函数,内部函数引用外部函数中的变量,外部函数的返回值是内部函数。闭包是将函数内部和函数外部连接起来的桥梁。

举例:比如我们调用一个带有返回值的函数x,此时函数x为我们返回一个函数y,这个函数y,就被称为闭包。

# 闭包 def out(i): # 一个外层函数 def inner(j): # 内层函数 return i*j return inner

闭包定义条件:

  • 必须有一个内嵌函数
  • 内嵌函数必须引用外部函数中的变量
  • 外部函数的返回值必须是内嵌函数

案例:函数test_in就是闭包

def test(number): print("--1--") def test_in(number2): print("--2--") print(number number2) print("--3--") return test_in ret = test(100) #先执行第3行,输出"--1--";再执行第5行,函数名(test_in),不执行函数内部;然后执行第10行,输出"--3--";然后返回(test_in)函数给(test)函数,赋值给ret对象。 print("-"*30) #输出”------------------------------“ ret(1) #执行(test_in)函数,执行第6行,输出"--2--";执行第7行,输出"101"(number=100,number2=1)。 ret(100) #执行(test_in)函数,执行第6行,输出"--2--";执行第7行,输出"200"(number=100,number2=100)。 ret(200) #执行(test_in)函数,执行第6行,输出"--2--";执行第7行,输出"300"(number=100,number2=200)。

输出结果:

--1-- --3-- ------------------------------ --2-- 101 --2-- 200 --2-- 300

运行分析:先调用外部函数test传入默认值number,用ret去指向返回函数内部的引用。后面在调用ret的时候,就会在调用外面函数的基础上进行计算。

2.什么是装饰器

装饰器就是装饰、装修的意思,不改变原有程序的功能。比如,我家有一个房子,如果不隔音,我在墙上加一层隔音板,而不是重新再建一座房子。这样一来,房子还是原来的,只是增加了一个隔音板(装饰器),实现了房子隔音的功能。

在程序中也是一样,不会对原来的函数造成变化,还要添加新的功能,调用函数时的接口没有变化。

装饰器可以基于函数实现,也可以基于类实现,其使用方式基本是固定的。它的基本步骤为:

  1. 定义装饰函数(类)
  2. 定义业务函数
  3. 在业务函数上一行添加@装饰函数名/类名

案例:(需求:在不动原函数的基础上增加新的功能)

def w1(func): """装饰器函数""" def inner(): func() print("这是添加的新功能") return inner @w1 # 等同于调用函数的时候上方加上:f1 = w1(f1) def f1(): """业务函数""" print("---f1---") @w1 # 等同于调用函数的时候上方加上:f2 = w1(f2) def f2(): """业务器函数""" print("---f2---") f1() f2()

输出结果:

---f1--- 这是添加的新功能 ---f2--- 这是添加的新功能

3.闭包与装饰器的区别

闭包传递的是 变量,而装饰器传递的是 函数,除此之外没有任何区别,或者说装饰器是闭包的一种,它只是 传递函数的闭包。

4.为何要用装饰器

开放封闭原则开放:指的是对拓展功能是开放的封闭:指的是对修改源代码是封闭的

装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下,为被装饰对象添加新功能。

5.适用场景

函数在执行前后定义一些功能。


三、如何使用装饰器1.通过案例了解装饰器的用法

案例:

def w1(fn): """装饰器函数""" print("---正在装饰---") def inner(): print("---正在验证---") fn() return inner @w1 # 只要Python解释器执行到这个代码,就开始自动进行装饰,而不是等到调用的时候才装饰 def f1(): """业务函数""" print("---2---") return "hello python"

输出结果:

---正在装饰---

运行分析:

代码执行到@w1就开始装饰了,我们并没有调用函数f1都输出了“---正在装饰---”,我们调用的是装饰后的结果。


案例:

def w1(fn): """装饰器函数""" print("---正在装饰1---") def inner(): print("---正在验证---") fn() return inner def w2(fn): """装饰器函数""" print("---正在装饰2---") def inner(): print("---正在验证---") fn() return inner @w1 @w2 def f1(): """业务函数""" print("---3---")

输出结果:

---正在装饰2--- ---正在装饰1---

运行分析:

@w1在最上面,下面需要是一个函数,可下面是@w2,必须等@w2装饰完再装饰@w1,所以先输出 ---正在装饰2---,再输出 ---正在装饰1---。

2.Python装饰器执行顺序详解

2.1案例执行

案例:

def decorator_a(func): print ('Get in decorator_a') def inner_a(*args, **kwargs): print ('Get in inner_a') return func(*args, **kwargs) return inner_a def decorator_b(func): print ('Get in decorator_b') def inner_b(*args, **kwargs): print ('Get in inner_b') return func(*args, **kwargs) return inner_b @decorator_b @decorator_a def f(x): print ('Get in f') return x * 2 f(1)

输出结果:

Get in decorator_a Get in decorator_b Get in inner_b Get in inner_a Get in f

运行分析:

上面代码先定义两个函数: decotator_a, decotator_b, 这两个函数实现的功能是,接收一个函数作为参数,然后返回创建的另一个函数;

在这个创建的函数里,调用接收的函数(文字比代码绕人)。最后定义的函数 f 采用上面定义的decotator_a, decotator_b作为装饰函数。

在当我们以1为参数,调用装饰后的函数 f 后, decotator_a, decotator_b 的顺序是什么呢(这里为了表示函数执行的先后顺序,采用打印输出的方式来查看函数的执行顺序)?

如果不假思索,根据自下而上的原则来判断地话,先执行 decorator_a 再执行 decorator_b , 那么会先输出 Get in decotator_a, Get in inner_a 再输出 Get in decotator_b, Get in inner_b 。然而事实并非如此。

2.2函数和函数调用的区别

为什么是先执行inner_b 再执行inner_a 呢?为了彻底看清上面的问题,得先分清两个概念:函数和函数调用。

上面的例子中 f 称之为函数, f(1) 称之为函数调用,后者是对前者传入参数进行求值的结果。在Python中函数也是一个对象,所以 函数f 是指代一个函数对象,它的值是函数本身, f(1) 是对函数的调用,它的值是调用的结果,这里的定义下 f(1) 的值为2。同样地,拿上面的decorator_a函数来说,它返回的是个函数对象inner_a ,这个函数对象是它内部定义的。在inner_a 里调用了函数 func ,将 函数func 的调用结果作为值返回。

2.3装饰器函数在被装饰函数定义好后立即执行

当装饰器装饰一个函数时,究竟发生了什么。现在简化我们的例子,假设是下面这样的:

def decorator_a(func): print ('Get in decorator_a') def inner_a(*args, **kwargs): print ('Get in inner_a') return func(*args, **kwargs) return inner_a @decorator_a def f(x): print ('Get in f') return x * 2

解读:

@decorator_a #等同于调用函数的时候上方加上:f=decorator_a(f) def f(x): print ('Get in f') return x * 2 # 相当于 def f(x): print ('Get in f') return x * 2 f = decorator_a(f)

运行分析:

当解释器执行这段代码时, decorator_a 已经调用了,它把函数名f 作为参数传递给形参fun, 返回它内部生成的一个函数inner_a,所以此后 f 指代的是 decorater_a 里面返回的 inner_a。所以当以后调用 函数f 时,实际上相当于调用 inner_a,传给函数 f 的参数会传给函数inner_a, 在调用函数inner_a 时,会把接收到的参数(函数名 f)传给inner_a里的 形参func=f ,最后返回的是 调用函数f 的值,所以在最外面看起来就像是直接调用函数 f 一样。

2.4案例执行顺序分析

当理清上面两方面概念时,就可以清楚地看清最原始的例子中发生了什么。当解释器执行下面这段代码时,实际上按照从下到上的顺序已经依次调用了 decorator_a和 decorator_b,这是会输出对应的 Get in decorator_a 和 Get in decorator_b。 这时候函数f 已经相当于 函数decorator_b里的 函数inner_b。但因为 函数 f 并没有被调用,所以 函数inner_b 并没有被调用,依次类推 函数inner_b内部的 函数inner_a也没有被调用,所以 Get in inner_a 和 Get in inner_b也不会被输出。

案例:

def decorator_a(func): print ('Get in decorator_a') def inner_a(*args, **kwargs): print ('Get in inner_a') return func(*args, **kwargs) return inner_a def decorator_b(func): print ('Get in decorator_b') def inner_b(*args, **kwargs): print ('Get in inner_b') return func(*args, **kwargs) return inner_b @decorator_b @decorator_a def f(x): print ('Get in f') return x * 2 f(1)

输出结果:

Get in decorator_a Get in decorator_b Get in inner_b Get in inner_a Get in f

然后第21行,当我们对函数 f 传入参数1进行调用时,函数inner_b被调用了,它会先打印 Get in inner_b,然后在函数 inner_b 内部调用了函数inner_a,所以会再打印 Get in inner_a, 然后在函数 inner_a 内部调用的原来的函数 f, 并且将结果作为最终的返回值。这时候你该知道为什么输出结果会是那样,以及对装饰器执行顺序实际发生了什么有一定了解了吧。

2.5案例执行顺序总结

-多个装饰器执行的顺序,是按照从下到上的顺序执行装饰器,再执行函数本身。

-装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。

15~19行是装载装饰器的过程,相当于执行了f=decorator_b(decorator_a(f)):

  1. 此时先执行decorator_a(f),结果是输出 Get in decorator_a,将形参func指向函数f、并返回函数inner_a;
  2. 然后执行函数 decorator_b(inner_a),结果是输出 Get in decorator_b,将形参 func指向函数inner_a、并返回函数inner_b;
  3. 函数 f本身相当于函数inner_b。

21行则是实际调用被装载的函数,用函数名 inner_b替代了函数名f。

  1. 这时实际上执行的是函数inner_b;
  2. 运行到函数func()时执行函数inner_a;
  3. 再运行到函数 func()时执行未修饰的函数f。
四、装饰器传参

案例:传递2个参数

def w1(fn): """装饰器函数""" print("---正在装饰---") def inner(a, b): # 如果a, b没有定义,那么会导致19行代码调用失败 print("---正在验证---") fn(a, b) # 如果没有把a, b当实参进行传递,那么会导致调用13行的函数失败 return inner @w1 def f1(a, b): """业务函数""" print(a b) f1(10, 20)

输出结果:

---正在装饰--- ---正在验证--- 30


案例:不定长参数

def w1(fn): """装饰器函数""" print("---正在装饰---") def inner(*args, **kwargs): # 如果a, b没有定义,那么会导致19行代码调用失败 print("---正在验证---") fn(*args, **kwargs) # 如果没有把a, b当实参进行传递,那么会导致调用13行的函数失败 return inner @w1 def f1(a, b): """业务函数""" print(a b) @w1 def f2(a, b, c): """业务函数""" print(a b c) f1(10, 20) f2(10, 20, 30)

输出结果:

---正在装饰--- ---正在装饰--- ---正在验证--- 30 ---正在验证--- 60

五、装饰器返回值

案例 :

def w1(fn): """装饰器函数""" print("---正在装饰---") def inner(): print("---正在验证---") ret = fn() # 保存返回来的字符串 return ret # 把字符串返回到20行的调用处 return inner @w1 def test(): """业务函数""" print("---test---") return "这是原函数返回值" ret = test() # 需要用参数来接收返回值 print(ret)

输出结果:

---正在装饰--- ---正在验证--- ---test--- 这是原函数返回值

六、通用装饰器

案例:

def w1(fn): """装饰器函数""" def inner(*args, **kwargs): print("---记录日志---") ret = fn(*args, **kwargs) # 保存返回来的字符串 return ret # 把字符串返回到20行的调用处 return inner @w1 def test1(): """不带返回值""" print("---test1---") @w1 def test2(): """带返回值""" print("---test2---") return "这是原函数返回值" @w1 def test3(a): """业务函数""" print("---test3中的数据:%d---" % a) ret1 = test1() print(ret1) ret2 = test2() print(ret2) ret3 = test3(10) print(ret3)

输出结果:

---记录日志--- ---test1--- None ---记录日志--- ---test2--- 这是原函数返回值 ---记录日志--- ---test3中的数据:10--- None

七、装饰器带参数

案例:

def func_arg(arg): def func(funtionName): def func_in(): print("输出给装饰器传入的参数:%s" % arg) if arg == "hello": funtionName() funtionName() else: funtionName() return func_in return func @func_arg("hello") def test(): print("---test---") @func_arg("haha") def test2(): print("---test2---") test() test2()

输出结果:

输出给装饰器传入的参数:hello ---test--- ---test--- 输出给装饰器传入的

运行分析:

  1. 先执行func_arg("hello")函数,这个函数return的结果是func这个函数的引用;
  2. 执行@func;
  3. 使用@func对函数test进行装饰
,