zoukankan      html  css  js  c++  java
  • 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()
    

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

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

  • 相关阅读:
    leetcode 买卖股票的最佳时机3
    leetcode 买卖股票的最佳时机Ⅱ
    leetcode 最长有效括号
    C++中的%lld和%I64d区别
    Ural 1095 Nikifor 3 思维+同余性质的利用
    博弈基础
    ural 1091. Tmutarakan Exams
    容斥原理
    一些易错的地方
    codeforces911D Inversion Counting 求逆序数+小trick
  • 原文地址:https://www.cnblogs.com/minseo/p/6826021.html
Copyright © 2011-2022 走看看