zoukankan      html  css  js  c++  java
  • 各种装饰器demo及优化

    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # @Time    : 2018/5/28 14:06
    # @File    : lianxi.py


    #
    # import time
    # def decorator(func):
    #     def wrapper(*args, **kwargs):
    #         start_time = time.time()
    #         func()
    #         end_time = time.time()
    #         print(end_time - start_time)
    #
    #     return wrapper
    #
    # @decorator
    # def func():
    #     time.sleep(0.8)
    #
    # func() # 函数调用

    # 示例一

    # #既不需要侵入,也不需要函数重复执行
    # import time
    #
    # def deco(func):
    #     def wrapper():
    #         startTime = time.time()
    #         func()
    #         endTime = time.time()
    #         msecs = (endTime - startTime)*1000
    #         print("time is %d ms" %msecs)
    #     return wrapper
    #
    #
    # @deco
    # def func():
    #     print("hello")
    #     time.sleep(1)
    #     print("world")
    #
    # if __name__ == '__main__':
    #     f = func #这里f被赋值为func,执行f()就是执行func()
    #     f()



    # #带有参数的装饰器
    # import time
    #
    # def deco(func):
    #     def wrapper(a,b):
    #         startTime = time.time()
    #         func(a,b)
    #         endTime = time.time()
    #         msecs = (endTime - startTime)*1000
    #         print("time is %d ms" %msecs)
    #     return wrapper
    #
    #
    # @deco
    # def func(a,b):
    #     print("hello,here is a func for add :")
    #     time.sleep(1)
    #     print("result is %d" %(a+b))
    #
    # if __name__ == '__main__':
    #     f = func
    #     f(3,4)
    #     #func()


    # #带有不定参数的装饰器
    # import time
    #
    # def deco(func):
    #     def wrapper(*args, **kwargs):
    #         startTime = time.time()
    #         func(*args, **kwargs)
    #         endTime = time.time()
    #         msecs = (endTime - startTime)*1000
    #         print("time is %d ms" %msecs)
    #     return wrapper
    #
    #
    # @deco
    # def func(a,b):
    #     print("hello,here is a func for add :")
    #     time.sleep(1)
    #     print("result is %d" %(a+b))
    #
    # @deco
    # def func2(a,b,c):
    #     print("hello,here is a2 func for add :")
    #     time.sleep(1)
    #     print("result2 is %d" %(a+b+c))
    #
    #
    # if __name__ == '__main__':
    #     f = func
    #     func2(3,4,5)
    #     f(3,4)
    #     #func()
    # 运行结果
    # hello,here is a2
    # func
    # for add:
    #     result2 is 12
    # time is 1000
    # ms
    # hello,here is a
    # func
    # for add:
    #     result is 7
    # time is 1000
    # ms



    #
    # #多个装饰器
    #
    # import time
    #
    # def deco01(func):
    #     def wrapper(*args, **kwargs):
    #         print("this is deco01")
    #         startTime = time.time()
    #         func(*args, **kwargs)
    #         endTime = time.time()
    #         msecs = (endTime - startTime)*1000
    #         print("time is %d ms" %msecs)
    #         print("deco01 end here")
    #     return wrapper
    #
    # def deco02(func):
    #     def wrapper(*args, **kwargs):
    #         print("this is deco02")
    #         func(*args, **kwargs)
    #
    #         print("deco02 end here")
    #     return wrapper
    #
    # @deco01
    # @deco02
    # def func(a,b):
    #     print("hello,here is a func for add :")
    #     time.sleep(1)
    #     print("result is %d" %(a+b))
    #
    #
    #
    # if __name__ == '__main__':
    #     f = func
    #     f(3,4)
    #     #func()
    #
    # # 注意执行顺序
    # '''
    # this is deco01
    # this is deco02
    # hello,here is a func for add :
    # result is 7
    # deco02 end here
    # time is 1000 ms
    # deco01 end here
    # '''



    # 示例二
    # 版本一1.1
    # def debug(func):
    #     def wrapper():
    #         print( "[DEBUG]: enter {}()".format(func.__name__))
    #         return func()
    #     return wrapper
    #
    # def say_hello():
    #     print( "hello!")
    #
    # say_hello = debug(say_hello)  # 添加功能并保持原函数名不变
    # say_hello()
    # 1.2 版本
    # def debug(func):
    #     def wrapper():
    #         print("[DEBUG]: enter {}()".format(func.__name__))
    #         return func()
    #     return wrapper
    #
    # @debug
    # def say_hello():
    #     print("hello!")
    # say_hello()

    # 2.0 版本
    # def debug(func):
    #     def wrapper(something):  # 指定一毛一样的参数
    #         print( "[DEBUG]: enter {}()".format(func.__name__))
    #         return func(something)
    #     return wrapper  # 返回包装过函数
    #
    # @debug
    # def say(something):
    #     print( "hello {}!".format(something))
    #
    # say('decorator.......')


    # # 版本 3
    # def debug(func):
    #     def wrapper(*args, **kwargs):  # 指定宇宙无敌参数
    #         print( "[DEBUG]: enter {}()".format(func.__name__))
    #         print( 'Prepare and say...',)
    #         return func(*args, **kwargs)
    #     return wrapper  # 返回
    #
    # @debug
    # def say(something):
    #     print( "hello {}!".format(something))
    # @debug
    # def say2(something, aaa):
    #     print( "hello {},------{}!".format(something, aaa))
    #
    # say2('aaa', 'vvvv')
    # say('aaa')


    # # 版本 4
    # def logging(level):
    #     def wrapper(func):
    #         def inner_wrapper(*args, **kwargs):
    #             print( "[{level}]: enter function {func}()".format(level=level,func=func.__name__))
    #             return func(*args, **kwargs)
    #         return inner_wrapper
    #     return wrapper
    #
    # @logging(level='INFO')
    # def say(something):
    #     print( "say {}!".format(something))
    #
    # # 如果没有使用@语法,等同于
    # # say = logging(level='INFO')(say)
    #
    # @logging(level='DEBUG')
    # def do(something):
    #     print ("do {}...".format(something))
    #
    # if __name__ == '__main__':
    #     say('hello')
    #     do("my work")

    # # 版本 5
    # class logging(object):
    #     def __init__(self, func):
    #         self.func = func
    #
    #     def __call__(self, *args, **kwargs):
    #         print( "[DEBUG]: enter function {func}()".format(func=self.func.__name__))
    #         return self.func(*args, **kwargs)
    # @logging
    # def say(something):
    #     print( "say {}!".format(something))
    #
    # say('hello')

    # 版本  6
    class logging(object):
        def __init__(self, level='INFO'):
            self.level = level

        def __call__(self, func):  # 接受函数
            def wrapper(*args, **kwargs):
                print("[{level}]: enter function {func}()".format(level=self.level,func=func.__name__))

                func(*args, **kwargs)

            return wrapper  # 返回函数


    @logging(level='INFO')
    def say(something):
        print("say {}!".format(something))


    say('什么鬼')

    装饰器里的那些坑

    装饰器可以让你代码更加优雅,减少重复,但也不全是优点,也会带来一些问题。
    位置错误的代码

    让我们直接看示例代码。

    def html_tags(tag_name):
        print 'begin outer function.'
        def wrapper_(func):
            print "begin of inner wrapper function."
            def wrapper(*args, **kwargs):
                content = func(*args, **kwargs)
                print "<{tag}>{content}</{tag}>".format(tag=tag_name, content=content)
            print 'end of inner wrapper function.'
            return wrapper
        print 'end of outer function'
        return wrapper_

    @html_tags('b')
    def hello(name='Toby'):
        return 'Hello {}!'.format(name)

    hello()
    hello()

    在装饰器中我在各个可能的位置都加上了print语句,用于记录被调用的情况。你知道他们最后打印出来的顺序吗?如果你心里没底,那么最好不要在装饰器函数之外添加逻辑功能,否则这个装饰器就不受你控制了。以下是输出结果:

    begin outer function.
    end of outer function
    begin of inner wrapper function.
    end of inner wrapper function.
    <b>Hello Toby!</b>
    <b>Hello Toby!</b>

    错误的函数签名和文档

    装饰器装饰过的函数看上去名字没变,其实已经变了。

    def logging(func):
        def wrapper(*args, **kwargs):
            """print log before a function."""
            print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
            return func(*args, **kwargs)
        return wrapper

    @logging
    def say(something):
        """say something"""
        print "say {}!".format(something)

    print say.__name__  # wrapper

    为什么会这样呢?只要你想想装饰器的语法糖@代替的东西就明白了。@等同于这样的写法。

    say = logging(say)

    logging其实返回的函数名字刚好是wrapper,那么上面的这个语句刚好就是把这个结果赋值给say,say的__name__自然也就是wrapper了,不仅仅是name,其他属性也都是来自wrapper,比如doc,source等等。

    使用标准库里的functools.wraps,可以基本解决这个问题。

    from functools import wraps

    def logging(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            """print log before a function."""
            print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
            return func(*args, **kwargs)
        return wrapper

    @logging
    def say(something):
        """say something"""
        print "say {}!".format(something)

    print say.__name__  # say
    print say.__doc__ # say something

    看上去不错!主要问题解决了,但其实还不太完美。因为函数的签名和源码还是拿不到的。

    import inspect
    print inspect.getargspec(say)  # failed
    print inspect.getsource(say)  # failed

    如果要彻底解决这个问题可以借用第三方包,比如wrapt。后文有介绍。
    不能装饰@staticmethod 或者 @classmethod

    当你想把装饰器用在一个静态方法或者类方法时,不好意思,报错了。

    class Car(object):
        def __init__(self, model):
            self.model = model

        @logging  # 装饰实例方法,OK
        def run(self):
            print "{} is running!".format(self.model)

        @logging  # 装饰静态方法,Failed
        @staticmethod
        def check_model_for(obj):
            if isinstance(obj, Car):
                print "The model of your car is {}".format(obj.model)
            else:
                print "{} is not a car!".format(obj)

    """
    Traceback (most recent call last):
    ...
      File "example_4.py", line 10, in logging
        @wraps(func)
      File "C:Python27libfunctools.py", line 33, in update_wrapper
        setattr(wrapper, attr, getattr(wrapped, attr))
    AttributeError: 'staticmethod' object has no attribute '__module__'
    """

    前面已经解释了@staticmethod这个装饰器,其实它返回的并不是一个callable对象,而是一个staticmethod对象,那么它是不符合装饰器要求的(比如传入一个callable对象),你自然不能在它之上再加别的装饰器。要解决这个问题很简单,只要把你的装饰器放在@staticmethod之前就好了,因为你的装饰器返回的还是一个正常的函数,然后再加上一个@staticmethod是不会出问题的。

    class Car(object):
        def __init__(self, model):
            self.model = model

        @staticmethod
        @logging  # 在@staticmethod之前装饰,OK
        def check_model_for(obj):
            pass



    如何优化你的装饰器

    嵌套的装饰函数不太直观,我们可以使用第三方包类改进这样的情况,让装饰器函数可读性更好。
    decorator.py

    decorator.py 是一个非常简单的装饰器加强包。你可以很直观的先定义包装函数wrapper(),再使用decorate(func, wrapper)方法就可以完成一个装饰器。

    from decorator import decorate

    def wrapper(func, *args, **kwargs):
        """print log before a function."""
        print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
        return func(*args, **kwargs)

    def logging(func):
        return decorate(func, wrapper)  # 用wrapper装饰func

    你也可以使用它自带的@decorator装饰器来完成你的装饰器。

    from decorator import decorator

    @decorator
    def logging(func, *args, **kwargs):
        print "[DEBUG] {}: enter {}()".format(datetime.now(), func.__name__)
        return func(*args, **kwargs)

    decorator.py实现的装饰器能完整保留原函数的name,doc和args,唯一有问题的就是inspect.getsource(func)返回的还是装饰器的源代码,你需要改成inspect.getsource(func.__wrapped__)。
    wrapt

    wrapt是一个功能非常完善的包,用于实现各种你想到或者你没想到的装饰器。使用wrapt实现的装饰器你不需要担心之前inspect中遇到的所有问题,因为它都帮你处理了,甚至inspect.getsource(func)也准确无误。

    import wrapt

    # without argument in decorator
    @wrapt.decorator
    def logging(wrapped, instance, args, kwargs):  # instance is must
        print "[DEBUG]: enter {}()".format(wrapped.__name__)
        return wrapped(*args, **kwargs)

    @logging
    def say(something): pass

    使用wrapt你只需要定义一个装饰器函数,但是函数签名是固定的,必须是(wrapped, instance, args, kwargs),注意第二个参数instance是必须的,就算你不用它。当装饰器装饰在不同位置时它将得到不同的值,比如装饰在类实例方法时你可以拿到这个类实例。根据instance的值你能够更加灵活的调整你的装饰器。另外,args和kwargs也是固定的,注意前面没有星号。在装饰器内部调用原函数时才带星号。

    如果你需要使用wrapt写一个带参数的装饰器,可以这样写。

    def logging(level):
        @wrapt.decorator
        def wrapper(wrapped, instance, args, kwargs):
            print "[{}]: enter {}()".format(level, wrapped.__name__)
            return wrapped(*args, **kwargs)
        return wrapper

    @logging(level="INFO")
    def do(work): pass

    关于wrapt的使用,建议查阅官方文档,在此不在赘述。

        http://wrapt.readthedocs.io/en/latest/quick-start.html

    小结

    Python的装饰器和Java的注解(Annotation)并不是同一回事,和C#中的特性(Attribute)也不一样,完全是两个概念。

    装饰器的理念是对原函数、对象的加强,相当于重新封装,所以一般装饰器函数都被命名为wrapper(),意义在于包装。函数只有在被调用时才会发挥其作用。比如@logging装饰器可以在函数执行时额外输出日志,@cache装饰过的函数可以缓存计算结果等等。

    而注解和特性则是对目标函数或对象添加一些属性,相当于将其分类。这些属性可以通过反射拿到,在程序运行时对不同的特性函数或对象加以干预。比如带有Setup的函数就当成准备步骤执行,或者找到所有带有TestMethod的函数依次执行等等。

    至此我所了解的装饰器已经讲完,但是还有一些内容没有提到,比如装饰类的装饰器。有机会再补充。谢谢观看。

  • 相关阅读:
    cf1131f 构造+并查集
    多源最短路小结
    bzoj2200拓扑排序+最短路+联通块
    cf478d 线性dp好题
    cf919D 线性dp+拓扑排序
    hiho1460 rmq模板题
    最小标示法模板 poj1509
    JAVA动态代理机制分析guide
    java动态代理(JDK和cglib)
    AOP面向切面编程
  • 原文地址:https://www.cnblogs.com/fmgao-technology/p/9100217.html
Copyright © 2011-2022 走看看