zoukankan      html  css  js  c++  java
  • python解答之装饰器

    装饰器,简而言之就是在不改变原函数任何代码(包括调用方式)的情况下为函数增加额外的功能。特殊之处在于装饰器的返回值也是一个函数。
    一、装饰器介绍
    下面先举个简单的栗子:

     1 import time
     2 
     3 
     4 def count_time(foo_func):
     5     def inner():
     6         print("inner func start!")
     7         start_time = time.time()
     8         time.sleep(1) # 1s后执行
     9         foo_func()
    10         end_time = time.time()
    11         print("the func cost:{:.2f}".format(end_time-start_time))
    12     return inner
    13 
    14 
    15 @count_time
    16 def foo():
    17     # 利用装饰器计算foo函数执行时间
    18     print("foo execute done!!")
    19 
    20 
    21 if __name__ == '__main__':
    22     foo() # 调用foo函数
    基础栗子

    既然说增加了装饰器,那么原函数的代码和调用方式都没改变。
    其中 @count_time 就等同于 foo = count_time(foo)

    如果要给装饰器传参数。。如下:
    import time
    def outer(name):
        def count_time(foo_func):
            def inner():
                print("inner func start!")
                print("装饰器传进来的参数是:{}".format(name))
                start_time = time.time()
                 time.sleep(1) # 1s后执行
                 foo_func()
                 end_time = time.time()
                 print("the func cost:{:.2f}".format(end_time-start_time))
             return inner
         return count_time
    
    
    @outer(name="jack")
    def foo():
        # 利用装饰器计算foo函数执行时间
        print("foo execute done!!")
    
    
    if __name__ == '__main__':
        foo() # 调用foo函数
    给装饰器传参数

    若要给装饰器传参数,只需要再给函数增加一层函数就行了。

    但是由于使用装饰器后会改变函数的一些内置变量比如__name__,__doc__。看栗子:

    import time
    from functools import wraps
    
    
    def count_time(foo_func):
        # @wraps(foo_func)
        def inner():
            """
                now in inner func
            """
            print("inner func start!")
            start_time = time.time()
            time.sleep(1) # 1s后执行
            foo_func()
            end_time = time.time()
            print("foo func cost:{:.2f}".format(end_time-start_time))
        return inner
    
    
    @count_time
    def foo():
        """
            now in foo func
        """
        # 利用装饰器计算foo函数执行时间
        print("foo func doc is {}".format(foo.__doc__))  # 输出 now in inner func
        print("foo func name is {}".format(foo.__name__)) # 输出 inner
        print("foo execute done!!")
    
    
    if __name__ == '__main__':
        foo() # 调用foo函数
    栗子
    所以需要纠正回来,该如何呢?来

    只需要引入functools模块下的wraps函数
    在inner函数上面使用@wraps(func),即再次装饰下inner函数即可
    然后输出语句会变为:
    print("foo func doc is {}".format(foo.__doc__)) # 输出 now in foo func
    print("foo func name is {}".format(foo.__name__)) # 输出 foo


    要给被装饰的函数传参数:
    栗子如下:
    import time
    from functools import wraps
    
    
    def count_time(foo_func):
        @wraps(func)
        def inner(*args, **kwargs):
            """
            now in inner func
            """
            print("inner func start!")
            start_time = time.time()
            time.sleep(1) # 1s后执行
            foo_func(*args, **kwargs)
            end_time = time.time()
            print("foo func cost:{:.2f}".format(end_time-start_time))
        return inner
    
    
    @count_time
    def foo(name, age, *args, **kwargs):
        """
        now in foo func
        """
        # 利用装饰器计算foo函数执行时间
        print(name,age)
        print(args,kwargs)
        print("foo execute done!!")
    
    
    if __name__ == '__main__':
        foo("jack", 21, "student", department="信工学院") # 调用foo函数
    给函数传参数

    增加的地方有调用时传了四个参数,分别是位置参数和关键字参数(最后一个是关键字参数,关键字参数必须在位置参数之后传入)
    在装饰器内部的inner函数的参数那里增加了*args, **kwargs,和inner内部的func(*args, **kwargs)这里
    args表示所有的位置参数都在args这个元组里面
    kwargs表示所有的关键字参数都在args这个字典里面


    二、基于类实现的装饰器
    看栗子:
    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")
    基于类实现的装饰器

    输出:
    [DEBUG]: enter function say()
    say hello!
    解答:如果用类来实现装饰器的话,必须实现类中的内置方法__init__和__call__这两个方法
    其中__init__用来接收被装饰函数的名字,__call__用来对函数进行装饰以及增加额外的功能

    若要给装饰器传参数。还是看栗子:
    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("hello")
    传参数

    这里的调用就是给装饰器传参数。
    __init__方法参数接收的不再是被装饰函数的函数名,而是装饰器传来的参数
    __call__方法参数接收被装饰函数的函数名,里面再嵌套一层函数用来接收装饰函数传的参数

    最后,拓展一下wrapt这个装饰器
    import wrapt
    
    @wrapt.decorator
    def logging(wrapped, instance, args, kwargs):  # instance is must need
        print("[DEBUG]: enter {}()".format(wrapped.__name__))
        return wrapped(*args, **kwargs)
    
    @logging
    def say(something): pass
    
    say("hello")
    关于wrapt

    这样看起来装饰器就更加明了了,,不用在装饰器内部再嵌套函数了。
    上面的写法必须要按照上面的来
    其中wrapped就表示被装饰的函数,
    若要给装饰器传参数,也是再嵌套一层就可以了。






    努力有用的话,还要天才做什么呢?
  • 相关阅读:
    Android 实现Path2.0中绚丽的的旋转菜单
    Android SQLite数据库增删改查操作
    Android addRule()
    Android 实现全屏、无标题栏
    微信公众号开发教程
    HEAP CORRUPTION DETECTED
    Introduction to gaussian filter 高斯滤波器
    Windows 7硬盘安装CentOS 6.4 双系统 (WIN7硬盘安装Linux(Fedora 16,CentOS 6.2,Ubuntu 12.04))
    使用Scala操作Mongodb
    数字三角——递归、递归、内存搜索
  • 原文地址:https://www.cnblogs.com/crazy-xf/p/9597764.html
Copyright © 2011-2022 走看看