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的函数依次执行等等。

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

  • 相关阅读:
    Unique Binary Search Trees 解答
    Unique Paths II 解答
    Unique Paths 解答
    Maximum Subarray 解答
    Climbing Stairs 解答
    House Robber II 解答
    House Robber 解答
    Valid Palindrome 解答
    Container With Most Water 解答
    Remove Duplicates from Sorted List II 解答
  • 原文地址:https://www.cnblogs.com/fmgao-technology/p/9100217.html
Copyright © 2011-2022 走看看