装饰器--初识

Posted 风之岚翔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器--初识相关的知识,希望对你有一定的参考价值。

函数调用的顺序

和其他语言类似。python函数在未经声明之前,不允许对其引用和调用。

 1 1、未声明之前,调用或者引用会报错。
 2 def t1():
 3     print(\'This is t1...\')
 4     t2()
 5 
 6 t1()
 7 
 8 def t2():
 9     print(\'This is t2...\')
10 
11     
12 显示结果:
13 This is t1...
14 Traceback (most recent call last):
15   File "D:/软件/pychar/data/s13/practise1.py", line 37, in <module>
16     t1()
17   File "D:/软件/pychar/data/s13/practise1.py", line 35, in t1
18     t2()
19 NameError: name \'t2\' is not defined
20 
21 
22 
23 2、正确的调用顺序:(先声明函数,在引用或者调用)
24 def t1():
25     print(\'This is t1...\')
26     t2()
27 
28 def t2():
29     print(\'This is t2...\')
30 
31 t1()
32 
33 显示结果:
34 This is t1...
35 This is t2...

 

 

函数的功能与作用

需要添加一个打印日志功能。
在没学函数的时候,只能用print显示打印,例如:

1 def t1():
2     pass
3     print(\'logging\')
4    
5 def t2():
6     pass
7     print(\'logging\')

 

学了函数以后可以定义一个函数,例如:

def logger():
    print(\'logging\')

def t1():
    pass
   logger()   #直接调用函数
   
def t2():
    pass
    logger()

 

装饰器的功能与特点

对已经上线的代码需要添加功能:

问题:
1、代码行数很多,查找很麻烦,而且要挨个找出,把新功能函数加进去。
2、代码已经上线,对源代码修改可能会导致未知故障发生。(新增功能是不能修改源代码的)
3、代码已经上线,不光源代码不能随便修改,就连函数调用方式也不能随意修改。

解决方法:使用装饰器进行代码修饰。

 

装饰器的概述

一、定义:

装饰器本身就是一个函数,遵循函数的声明,引用,调用,作用域等相关规则。

二、作用:

装饰器:目的是(装饰其他函数)就是为其他函数添加附加功能。

三、用途:

装饰器:最常见的用途主要体现在用户登录与权限认证。

四、原则:

1、不能修改被装饰的函数的源代码。
2、不能修改被装饰的函数的调用方式。
3、装饰器对被装饰的函数时完全透明的。(源代码感知不到装饰器函数的存在,但装饰器函数确实影响着源代码)
 
 
五、调用方法:
@+装饰器的函数名;
一个原函数体可以调用多个装饰器,执行顺序如下:

 

 

 装饰器的知识储备

1、函数即"变量"
2、高阶函数。
3、嵌套函数。
最终:高阶函数+嵌套函数 =》装饰器函数

 

 知识点一:函数即“变量”:

定义一个x或者y是装在内存中声明一个内存块,并通通过存地址指定给x或者y,当调用x或者y时可以加载在内存块中进行引用或调用。

定义一个test的函数的原理同上。

定义一个无名子的函数体原理也同上。

注意:
解释器会先回收无名子的函数体所占的内存块(即lambda的函数模块)
del 并不是删除内存块,而是直接删除内存定义的名字,通过解析器在清除内存块。

 

 

知识点二、高阶函数:

满足下面两个条件之一就是高阶函数
a:把一个函数名当做实参传给另外一个函数,即某一函数当做参数传入另一个函数中(在不修改被装饰函数源代码的情况下添加功能)
b:返回值中包含函数名,即函数的返回值包含n个函数,n>0(不修改函数的调用方式)

练习a:

 1 #!/usr/bin/env python
 2 # -*- coding:utf8 -*-
 3 # Author:Dong Ye
 4 
 5 import time
 6 
 7 #定义一个源代码。要求不允许改变源代码和调用方式的情况下进行装饰:
 8 def bar():   #定义个变量,模拟线上函数体
 9     time.sleep(3)
10     print("in the bar")
11 
12 
13 #用高阶函数装饰bar代码
14 def test1(func):    #定义一个形参,用于接收调用test1函数的实参,如果传过来的是函数名,则该函数已经被应用。
15     start_time =  time.time()
16     func()         #调用bar函数,类似于x=1,y=x  显示y。而func = bar,直接调用func
17     stop_time = time.time()
18     print("the func run time is %s (non bar run time)" %(stop_time-start_time))
19 
20 test1(bar)  #调用仿装饰器传实参给test1(模拟线上环境的函数)
某一函数当做参数传入另一个函数中
重点:结合装饰器的重要的两个特点,做个练习1:
1、不能修改被装饰的函数的源代码。
2、不能修改被装饰的函数的调用方式。
 
问题:
1、装饰器并没有修改源代码bar
2、由于test1(bar)改变了调用函数的方式,因此不符合装饰器的条件。
 
注释:
test1(bar)与test1(bar())的区别:
test1(bar):是把内存地址通过实参方式传给test1函数作为形参。
test1(bar()):是把内存中的函数体通过实参方式传给test1函数作为形参。

 

 练习b:返回值中包含函数名。(不修改函数的调用方式)

 1 #!/usr/bin/env python
 2 # -*- coding:utf8 -*-
 3 # Author:Dong Ye
 4 \'\'\'
 5 import time
 6 
 7 #定义一个源代码。要求不允许改变源代码和调用方式的情况下进行装饰:
 8 def bar():   #定义个变量,模拟线上函数体
 9     time.sleep(3)
10     print("in the bar")
11 
12 
13 #用高阶函数装饰bar代码
14 def test1(func):    #定义一个形参,用于接收调用test1函数的实参,如果传过来的是函数名,则该函数已经被应用。
15     start_time =  time.time()
16     func()         #调用bar函数,类似于x=1,y=x  显示y。而func = bar,直接调用func
17     stop_time = time.time()
18     print("the func run time is %s (non bar run time)" %(stop_time-start_time))
19 
20 test1(bar)  #调用仿装饰器传实参给test1(模拟线上环境的函数)
21 
22 \'\'\'
23 
24 import time
25 
26 #定义模拟线上代码
27 def bar():
28     time.sleep(3)
29     print(\'in the bar\')
30 
31 
32 #用高阶函数做装饰器
33 def test2(func):
34     print(func) #打印bar的内存地址
35     return func  #返回test2的运行结果地址
36 
37 #调用方式一:展示返回值
38 print(test2(bar))
39 
40 #调用方式二:获取返回值
41 t = test2(bar)
42 print(t)
43 #注释:
44 #首先、test2函数把bar传到test2函数里,然后打印bar函数的内存地址。
45 #其次、将bar的内存地址return返回给t。
46 #最后、打印t接收的bar地址。
47 
48 #调用方式三:变量名覆盖
49 bar = test2(bar) #将装饰器返回bar()的内存地址,覆盖掉原函数bar
50 bar()  #原函数调用方式
51 
52 #注释:
53 #由于bar在源代码中已经定义了。
54 #为了不影响源代码函数的调用方式,只提取了bar一开始传实参的内存地址
55 #当装饰器函数的功能执行完毕后,在将bar的内存地址返回,重新赋值给变量
56 #最后、从原函数的被调用的角度来看并没有改变bar()的调用方式,
57 # 而是在原基础上利用装饰器返回的bar的内存值进行了第二次赋值。
58 #这样装饰器功能不仅能够与源代码完美结合,又能满足不修改源代码,不修改调用函数的方法。
View Code

 

 

 知识点三、嵌套函数:

 在一个函数体内创建另外一个函数,这种函数就叫内嵌函数(基于python支持静态嵌套域)

 1 #嵌套函数:
 2 #概念:在一个函数的函数体内,用def去声明一个子函数
 3 #作用:高阶函数+嵌套函数=装饰器
 4 def foo():
 5     print(\'in the foo\')
 6     def bar():
 7         print(\'in the bar\')
 8 
 9     #在调用bar函数的时候,就跟调用局部变量一样,只能在内部调用
10     bar()
11 foo()
12 
13 
14 
15 #局部作用域和全局作用域的访问顺序:
16 x = 0
17 def grandpa():
18     #x = 1
19     def dad(): #相当于定义了一个变量,如果不调用就相当于什么也没做。
20         x = 2
21         def son():
22             x = 3
23             print(x)  #这个值应该是3,因为只定义在son这个函数体里
24 
25         son()  #如果不调用就不会显示3,因为嵌套函数时一层一层的找。
26     dad()  #上面的定义如果不在这里调用,就等于什么也没做。
27 grandpa()  
嵌套函数练习

 

以上是关于装饰器--初识的主要内容,如果未能解决你的问题,请参考以下文章

初识python: 装饰器

5.初识python装饰器 高阶函数+闭包+函数嵌套=装饰器

python使用上下文对代码片段进行计时,非装饰器

初识装饰器

Python之第十八天的努力--装饰器初识

装饰器初识