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
#!/usr/bin/python # -*- coding:utf-8 -*- def f1(): print 'f1' f1()
业务部门调用基础平台提供的函数输出为f1
假如业务有需求要在调用前加一个验证(为了简化也用输出一个字符串代替),有几种方案
1,业务部门调用的时候加验证
修改代码
#!/usr/bin/python # -*- coding:utf-8 -*- def f1(): print 'f1' print 'before' f1()
每个部门在调用函数的时候自己加验证,很明显不行
2,修改底层函数
#!/usr/bin/python # -*- coding:utf-8 -*- def f1(): print 'before' print 'f1' f1()
业务部门不需要修改代码,但是底层函数有很多个,一个个修改不现实
3,重新定义一个函数,在底层函数一个个插入
#!/usr/bin/python # -*- coding:utf-8 -*- def before(): print 'before' def f1(): before() print 'f1' f1()
只需要在每个底层函数调用一次新的函数即可,好像可以了
但是
写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:
- 封闭:已实现的功能代码块
- 开放:对扩展开发
4,终极解决方案,使用装饰器
#!/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:装饰器返回的是一个函数体,不是函数执行后的结果,需要执行才能出结果
如下
#!/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是没有参数的
假如有函数是有参数的呢
重新定义一个带参数的装饰器即可
#!/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提供一种通用的装饰器方法无论提供多少个参数均可
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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()
单装饰器和多装饰器的运行结果如下,一层装饰器就是套一层盒子最上面的就是最外面的那层盒子
多装饰器的用途,用户登陆后的权限不同,一般用不上.