• PYTHON进阶装饰器小结,转载


    本文转载自:http://www.wklken.me/posts/2012/10/27/python-base-decorator.html

    基本概念

    具体概念自己google

    装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理, Web权限校验, Cache等。

    很有名的例子,就是咖啡,加糖的咖啡,加牛奶的咖啡。
    本质上,还是咖啡,只是在原有的东西上,做了“装饰”,使之附加一些功能或特性。

    例如记录日志,需要对某些函数进行记录

    笨的办法,每个函数加入代码,如果代码变了,就悲催了

    装饰器的办法,定义一个专门日志记录的装饰器,对需要的函数进行装饰,搞定

    优点

    抽离出大量函数中与函数功能本身无关的雷同代码并继续重用

    即,可以将函数“修饰”为完全不同的行为,可以有效的将业务逻辑正交分解,如用于将权限和身份验证从业务中独立出来

    概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能

    Python中的装饰器

    在Python中,装饰器实现是十分方便的

    原因是:函数可以被扔来扔去。

    函数作为一个对象:

    A.可以被赋值给其他变量,可以作为返回值
    
    B.可以被定义在另外一个函数内
    

    def:

    装饰器是一个函数,一个用来包装函数的函数,装饰器在函数申明完成的时候被调用,调用之后返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问(申明的函数被换成一个被装饰器装饰过后的函数)

    当我们对某个方法应用了装饰方法后, 其实就改变了被装饰函数名称所引用的函数代码块入口点,使其重新指向了由装饰方法所返回的函数入口点。

    由此我们可以用decorator改变某个原有函数的功能,添加各种操作,或者完全改变原有实现

    分类:

    装饰器分为无参数decorator,有参数decorator

    * 无参数decorator
    
    生成一个新的装饰器函数
    
    * 有参decorator
    
    有参装饰,装饰函数先处理参数,再生成一个新的装饰器函数,然后对函数进行装饰
    

    装饰器有参/无参,函数有参/无参,组合共4种

    具体定义:

    decorator方法

    A.把要装饰的方法作为输入参数,

    B.在函数体内可以进行任意的操作(可以想象其中蕴含的威力强大,会有很多应用场景),

    C.只要确保最后返回一个可执行的函数即可(可以是原来的输入参数函数, 或者是一个新函数)

    无参数装饰器 – 包装无参数函数

    不需要针对参数进行处理和优化

    def decorator(func):
        print "hello"
        return func
    
    @decorator
    def foo():
        pass
    
    foo()
    

    foo()
    等价于:

    foo = decorator(foo)
    foo()
    

    无参数装饰器 – 包装带参数函数

    def decorator_func_args(func):
        def handle_args(*args, **kwargs): #处理传入函数的参数
            print "begin"
            func(*args, **kwargs)   #函数调用
            print "end"
        return handle_args
    
    
    @decorator_func_args
    def foo2(a, b=2):
        print a, b
    
    foo2(1)
    

    foo2(1)
    等价于

    foo2 = decorator_func_args(foo2)
    foo2(1)
    

    带参数装饰器 – 包装无参数函数

    def decorator_with_params(arg_of_decorator):#这里是装饰器的参数
        print arg_of_decorator
        #最终被返回的函数
        def newDecorator(func):
            print func
            return func
        return newDecorator
    
    
    @decorator_with_params("deco_args")
    def foo3():
        pass
    foo3()
    

    与前面的不同在于:比上一层多了一层封装,先传递参数,再传递函数名

    第一个函数decomaker是装饰函数,它的参数是用来加强“加强装饰”的。由于此函数并非被装饰的函数对象,所以在内部必须至少创建一个接受被装饰函数的函数,然后返回这个对象(实际上此时foo3= decorator_with_params(arg_of_decorator)(foo3))

    带参数装饰器– 包装带参数函数

    def decorator_whith_params_and_func_args(arg_of_decorator):
        def handle_func(func):
            def handle_args(*args, **kwargs):
                print "begin"
                func(*args, **kwargs)
                print "end"
                print arg_of_decorator, func, args,kwargs
            return handle_args
        return handle_func
    
    
    @decorator_whith_params_and_func_args("123")
    def foo4(a, b=2):
        print "Content"
    
    foo4(1, b=3)
    

    内置装饰器

    内置的装饰器有三个:staticmethod,classmethod, property

    class A():
        @staticmethod
        def test_static():
            print "static"
        def test_normal(self):
            print "normal"
        @classmethod
        def test_class(cls):
            print "class", cls
    
    a = A()
    A.test_static()
    a.test_static()
    a.test_normal()
    a.test_class()
    

    结果:

    static
    static
    normal
    class __main__.A
    

    A.test_static

    staticmethod 类中定义的实例方法变成静态方法

    基本上和一个全局函数差不多(不需要传入self,只有一般的参数),只不过可以通过类或类的实例对象来调用,不会隐式地传入任何参数。

    类似于静态语言中的静态方法

    B.test_normal

    普通对象方法:
    普通对象方法至少需要一个self参数,代表类对象实例

    C.test_class

    类中定义的实例方法变成类方法

    classmethod需要传入类对象,可以通过实例和类对象进行调用。

    是和一个class相关的方法,可以通过类或类实例调用,并将该class对象(不是class的实例对象)隐式地当作第一个参数传入。

    就这种方法可能会 比较奇怪一点,不过只要你搞清楚了python里class也是个真实地存在于内存中的对象,而不是静态语言中只存在于编译期间的类型,就好办了。正常的方法就是和一个类的实例对象相关的方法,通过类实例对象进行调用,并将该实例对象隐式地作为第一个参数传入,这个也和其它语言比较像。

    D.区别

    staticmethod,classmethod相当于全局方法,一般用在抽象类或父类中。一般与具体的类无关。

    类方法需要额外的类变量cls,当有子类继承时,调用类方法传入的类变量cls是子类,而不是父类。

    类方法和静态方法都可以通过类对象和类的实例对象访问

    定义方式,传入的参数,调用方式都不相同。

    E.property

    对类属性的操作,类似于java中定义getter/setter

    class B():
        def __init__(self):
            self.__prop = 1
        @property
        def prop(self):
            print "call get"
            return self.__prop
        @prop.setter
        def prop(self, value):
            print "call set"
            self.__prop = value
        @prop.deleter
        def prop(self):
            print "call del"
            del self.__prop
    

    其他

    A.装饰器的顺序很重要,需要注意

    @A
    @B
    @C
    def f ():
    

    等价于

    f = A(B(C(f)))
    

    B.decorator的作用对象可以是模块级的方法或者类方法

    C.functools模块提供了两个装饰器。
    这个模块是Python 2.5后新增的。

    functools.wraps(func)
    total_ordering(cls)
    这个具体自己去看吧,后续用到了再补充

    一个简单例子

    通过一个变量,控制调用函数时是否统计时间

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    #@author: wklken@yeah.net
    #@version: a test of decorator
    #@date: 20121027
    #@desc: just a test
    
    
    import logging
    
    from time import time
    
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)
    is_debug = True
    
    def count_time(is_debug):
        def  handle_func(func):
            def  handle_args(*args, **kwargs):
                if is_debug:
                    begin = time()
                    func(*args, **kwargs)
                    logging.debug( "[" + func.__name__ + "] -> " + str(time() - begin) )
                else:
                    func(*args, **kwargs)
            return handle_args
        return handle_func
    
    def pr():
        for i in range(1,1000000):
            i = i * 2
        print "hello world"
    
    def test():
        pr()
    
    @count_time(is_debug)
    def test2():
        pr()
    
    @count_time(False)
    def test3():
        pr()
    
    if __name__ == "__main__":
        test()
        test2()
        test3()
    

    结果:

    hello world
    hello world
    DEBUG:root:[test2] -> 0.0748538970947
    hello world
    

    The end!

    wklken

    Gighub: https://github.com/wklken

    Blog: http://wklken.sinaapp.com/

    2012-10-27

    转载请注明出处,谢谢!

  • 相关阅读:
    ruby
    快捷键_命令行
    Babylon
    遇见的一些关于js冲突的问题
    js中出生年月日的使用,以及性别的判断
    js中iframe的使用
    js中ul与li的使用
    jQuery,ajax,for循环使用典型案例
    Easy | LeetCode 118. 杨辉三角
    Easy | LeetCode 461. 汉明距离 | 位运算
  • 原文地址:https://www.cnblogs.com/buxizhizhoum/p/6514842.html
Copyright © 2020-2023  润新知