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()

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

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

 

posted @ 2017-05-08 17:14  minseo  阅读(222)  评论(0编辑  收藏  举报