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()
单装饰器和多装饰器的运行结果如下,一层装饰器就是套一层盒子最上面的就是最外面的那层盒子
多装饰器的用途,用户登陆后的权限不同,一般用不上.
