学以致用三十六-----弄懂python装饰器

看了海峰老师讲解的装饰器视频,讲解的非常棒。根据视频,记录笔记如下:

 装饰器:

1、本质是函数,用def来定义。功能就是用来(装饰)其他函数,为其他函数添加附加功能

现有两个函数如下,

1 def test1():
2     pass
3 
4 def test2()
5     pass
6 
7 test1()
8 test2()

需要添加一个记录日志的功能

原始方法,在每个函数里添加

 1  def test1():
 2      pass
 3      print('logging')
 4  
 5  def test2()
 6      pass
 7      print('logging')
 8  
 9  test1()
10  test2()

再进一步,增加一个函数,test1和test2来调用

 1 def logger():
 2     print('logging')
 3 
 4 def test1():
 5        pass
 6        logger()
 7   
 8 def test2()
 9        pass
10        logger()
11   
12  test1()
13  test2()

那假如有上百上千个函数,并且是正常在线的代码,也这样处理吗?很显然这是不可能的

因此装饰器的原则

2、a. 不能修改被装饰的函数的源代码

      b.不能修改被装饰的函数的调用方式

实现装饰器知识储备:

3、a. 函数即“变量”

      b. 高阶函数

      c.嵌套函数

高阶函数+嵌套函数==装饰器

============================================

再来看一下以下几个场景

场景一

1 def bar():
2     print("in the bar")
3 
4 def foo():
5     print ("in the foo")
6     bar()
7     
8 foo()

场景二

1 def foo():
2     print ("in the foo")
3     bar()
4     
5 def bar():
6     print("in the bar")
7 
8 foo()

场景一和场景二的不同之处在于,一个bar()在foo()之前,一个在foo()之后,但两者结果一样,按理说python应该是从上往下一行一行执行,为啥场景二也没有问题呢

经过断点debug调试,发现在debug的过程中,程序会先把函数都过一遍,顺序是   def  foo()  ---->  def bar()  ---->  foo()  ----> print ("in the foo")   ---> bar() -----> print (in the bar)

那再看场景三

1 def foo():
2     print ("in the foo")
3     bar()
4 
5 foo()
6 
7 def bar():
8     print("in the bar")

按照之前的解释,程序也会先把函数都过一遍,然后去执行。但场景三确报错了,会提示 name 'bar' is not defined,debug显示 def foo() 后直接到 foo(), 就直接执行foo()函数了,并没有往下走。

因为遇到foo(),就表示要去执行foo函数,而此时 bar并未在内存建立‘’门牌号‘’,因此会报错

看下面这张图,函数相当于变量的集合。

 

高阶函数:

a:把一个函数名当做实参传给另外一个函数

(在不修改被装饰函数源代码的情况下为其添加功能)

b:返回值中包含函数名(不修改函数的调用方式),一定要有return,返回该函数的内存地址

如下面的代码

 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print('in the bar')
 5 
 6 def test1(func):
 7     start_time = time.time()
 8     func()
 9     stop_time = time.time()
10     print("the func run time is %s" %(stop_time-start_time))
11 
12 test1(bar)

此时相当于   func=bar, func()  会执行bar函数

执行结果

in the bar
the func run time is 3.0004918575286865

返回值包含函数名,如

 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print('in the bar')
 5 
 6 def test2(func):
 7     print(func)
 8     return func
 9 
10 test2(bar)
11 print("================")
12 print(test2(bar))

结果

<function bar at 0x000001B343BF3EA0>
================
<function bar at 0x000001B343BF3EA0>
<function bar at 0x000001B343BF3EA0>

 print(test2(bar))先打印 func,然后返回func的内存地址。于是又打印一遍  

换一种方式

 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print('in the bar')
 5 
 6 def test2(func):
 7     print(func)
 8     return func
 9 
10 t=test2(bar)
11 print("================")
12 # print(test2(bar))
13 t()

此时会去执行bar函数

结果

<function bar at 0x00000246AECB3EA0>
================
in the bar

那把 t  换成  bar 呢

 1 import time
 2 def bar():
 3     time.sleep(3)
 4     print('in the bar')
 5 
 6 def test2(func):
 7     print(func)
 8     return func
 9 
10 bar=test2(bar)
11 print("================")
12 # print(test2(bar))
13 bar()

结果和上面是一样的。可以看出函数的调用方式没有改变,结果是一样的

因此引出了装饰器

 1 import time
 2 def test2(func):
 3     # print(func)
 4     print("auth---")
 5     return func
 6 
 7 @test2
 8 def bar():
 9     time.sleep(1)
10     print('in the bar')
11 bar()

执行结果:

auth---
in the bar

在 in  the  bar 前面加了一段auth---,没有改变bar()的代码

这样装饰器就解释明白了

当然还有多层嵌套和装饰,带参数的装饰器,以后需要加强练习再补充。

原文地址:https://www.cnblogs.com/liongong/p/9927048.html