Python装饰器

 

Python装饰器,递归,模块

先看一个Python执行过程

>>> def foo():            #定义函数

...    print 'foo'           #执行函数打印字符串foo 
...
>>> foo
<function foo at 0x7fd3a06f77d0>  #表示foo是一个函数
>>> foo()              #执行函数输出  
foo

 

重新定义foo

>>>foo = lambda x:x + 1

>>> foo
<function <lambda> at 0x7fd3a06f75f0>
>>> foo(1)
2                   #foo函数被重新定义了,执行的是+1的函数

 

在看一个列子

>>> def f1(arg):
...    arg()
...
>>> def func():
...    print '12'
...
>>> f1(func)
12

执行步骤为

1,定义函数f1放入内存改函数传递一个参数arg 执行arg()代表执行一个函数

2,定义函数func 该函数执行打印数字12

3,执行函数f1调用func为参数,然后在f1内部加()执行func函数,相当于func(),结果就是打印出12

 

根据这个原理提出以下需求

假设公司有一个基础平台,基础平台提供底层的功能函数(比如调用数据库,监控API等)这里为了简化就定义为输出一个字符串

(也只定义了一个函数,实际工作肯定有多个函数)

vim day5-1.py

1
2
3
4
5
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
    print 'f1'
f1()

业务部门调用基础平台提供的函数输出为f1

假如业务有需求要在调用前加一个验证(为了简化也用输出一个字符串代替),有几种方案

1,业务部门调用的时候加验证

  修改代码

1
2
3
4
5
6
7
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
    print 'f1'
 
print 'before'
f1()

每个部门在调用函数的时候自己加验证,很明显不行

2,修改底层函数

1
2
3
4
5
6
7
#!/usr/bin/python
# -*- coding:utf-8 -*-
def f1():
    print 'before'
    print 'f1'
 
f1()

业务部门不需要修改代码,但是底层函数有很多个,一个个修改不现实

3,重新定义一个函数,在底层函数一个个插入

1
2
3
4
5
6
7
8
#!/usr/bin/python
# -*- coding:utf-8 -*-
def before():
    print 'before'
def f1():
    before()
    print 'f1'
f1()

只需要在每个底层函数调用一次新的函数即可,好像可以了

但是

写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

  • 封闭:已实现的功能代码块
  • 开放:对扩展开发

4,终极解决方案,使用装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):         #把函数auth写入内存
    def inner():        #函数内部函数
        print 'before'  #实现类似于验证的功能
        func()          #执行func()其实这里就是执行了f1()
    return inner        #返回内部函数inner
@auth                   #装饰器方法,调用auth函数
 
def f1():               #定义底层函数这里底层函数遵守封闭性原则不做任何改动
    print 'f1'
 
f1()                    #模拟各个部门调用底层函数,也未做改动

定义装饰器的执行过程如下

  一,把auth函数定义到内存

  二,定义底层函数f1

  三,使用@加函数名调用装饰器把函数f1作为参数传递给函数auth

  四,装饰器内部先执行print 'before' 在执行f1() 返回函数inner相当于把函数f1作为参数传递给函数auth然后把返回值在赋值给f1函数

  五,调用函数f1此时的输出为执行完验证以后的输出

完美实现了功能,底层定义函数代码及各个部门调用函数代码没有变化

 

装饰器其实就是函数加Python的语法糖

PS:装饰器返回的是一个函数体,不是函数执行后的结果,需要执行才能出结果

如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):      
    def inner():     
        print 'before'
        func()       
    return inner     
 
def f1():            
    print 'f1'
 
 
f1=auth(f1)          
f1()

把f1作为函数auth的参数然后在把返回的函数值赋值给f1,最后在执行一次f1函数出结果(比较low)

 

以上例子调用的函数f1是没有参数的

 假如有函数是有参数的呢

重新定义一个带参数的装饰器即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):      
    def inner():     
        print 'before'
        func()       
    return inner     
 
def auth_arg(func):
    def inner(arg):
        print 'before'
        func(arg)
    return inner
 
@auth                
 
def f1():            
    print 'f1'
 
#f1()                
@auth_arg
def f5(arg):
    print 'f5',arg

如果有多个参数使用上面的方式需要定义多个函数

Python提供一种通用的装饰器方法无论提供多少个参数均可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python
# -*- coding:utf-8 -*-
def auth(func):              
    def inner(*args,**kwargs):
        print 'before'       
        func(*args,**kwargs) 
    return inner             
 
@auth                        
 
def f1():                    
    print 'f1'
 
#f1()                        
@auth
def f5(arg):
    print 'f5',arg

 

小结

1,装饰器是一个函数,至少两层

2,执行auth函数,被装饰的函数作为参数auth(foo)

   auth函数的返回值,赋值给被装饰的函数的函数名

3,动态参数,可以装饰含有n个参数的函数

4,函数的返回值

5,多装饰器

 

以上调用的函数的没有返回值的假如调用的基础函数有返回值呢

vim basic.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/python
# -*- coding:utf-8 -*-
def login():
    name = 'alex'
    if name == 'alex':
        return True
    else:
        return False
def auth(func):                    
    def inner(*args,**kwargs):     
        is_login = login()
        if not is_login:
            return '非法用户'
        print 'before'             
        temp = func(*args,**kwargs)
        return temp                
    return inner                   
 
@auth                              
 
def f1():                          
    print 'f1'
 
#f1()                              
@auth
def f5(arg):
    print 'f5',arg
 
@auth
def fetch_server_list(arg):
    server_list = ['c1','c2','c3']
    return server_list

在auth里面返回了原函数的返回值,并且模拟了一个验证的过程

vim day5-3.py

1
2
3
4
5
#!/usr/bin/python
# -*- coding:utf-8 -*-
import basic
ret_list = basic.fetch_server_list('test')
print ret_list

调用输出

如果用户名不是alex则会输出非法用户的提示

PS:在写web项目的时候都有使用装饰器来做验证的作用.

 

再次模拟使用以后key来验证的过程

vim basic.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#!/usr/bin/python
# -*- coding:utf-8 -*-
def login(token):
    local = 'askjdhkjahsdkjahsakjsd'
    if local == token:
        return True
    else:
        return False
def auth(func):
    #fetch_server_list('test',token=key)               
    def inner(*args,**kwargs):
#        key = kwargs["token"]          #因为原函数fetch_server_list只接受一个参数     
#        del kwargs['token']            #所以把传递的字典的一个参数去掉,该参数在验证的时候已经使用过一次
        key = kwargs.pop('token')       #这句等同于以上两句
        is_login = login(key)
        if not is_login:
            return '非法用户'
        print 'before'
        temp = func(*args,**kwargs)
        return temp
    return inner
 
@auth
 
def f1():
    print 'f1'
 
#f1()                                  
@auth
def f5(arg):
    print 'f5',arg
 
@auth
def fetch_server_list(arg):
    server_list = ['c1','c2','c3']
    return server_list

vim day5-3.py

1
2
3
4
5
6
#!/usr/bin/python
# -*- coding:utf-8 -*-
import basic
key = 'askjdhkjahsdkjahsakjsd'
ret_list = basic.fetch_server_list('test',token=key)
print ret_list

模拟传递一个key进行验证,输出结果不变

 

多装饰器

vim day5-5.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
# -*- coding:utf-8 -*-
def w1(func):
    def inner():
        print 'w1,before'
        func()
        print 'w1,after'
    return inner
 
def w2(func):
    def inner():
        print 'w2,before'
        func()
        print 'w2,after'
    return inner
 
#@w2
@w1
def foo():
    print 'foo'
 
foo()

单装饰器和多装饰器的运行结果如下,一层装饰器就是套一层盒子最上面的就是最外面的那层盒子

多装饰器的用途,用户登陆后的权限不同,一般用不上.

 

posted @   minseo  阅读(226)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示