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() |
单装饰器和多装饰器的运行结果如下,一层装饰器就是套一层盒子最上面的就是最外面的那层盒子
多装饰器的用途,用户登陆后的权限不同,一般用不上.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 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代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!