zoukankan      html  css  js  c++  java
  • 装饰器

    参考:https://www.cnblogs.com/chenhuabin/p/11369359.html#_label1_0

    一、定义

    1.1 函数的四个特性

    • 可以赋值给其他变量
    • 可以作为参数传递给其他函数
    • 可以在内部定义一个函数
    • 可以当做返回值返回

    装饰器的本质是函数,主要用来装饰其他函数,也就是为其他函数添加附加功能。

    1.2 不用装饰器实现功能

    def out_func(f):
        def inner_func():
            print('{}函数开始运行……'.format(f.__name__))
            f()
            print('{}函数结束运行……'.format(f.__name__))
        return inner_func
    
    
    def func():
        print('正在完成功能')
    
    
    if __name__ == '__main__':
        ceshi = out_func(func)
        ceshi()

      

    1.3 使用装饰器实现功能

    def out_func(f):
        def inner_func():
            print('{}函数开始运行……'.format(f.__name__))
            f()
            print('{}函数结束运行……'.format(f.__name__))
        return inner_func
    
    @out_func
    def func():
        print('正在完成功能')
    
    if __name__ == '__main__':
        func()

    1.4 结论

    加了out_func装饰器的func 等价于 out_func(func)

    二、装饰器的优点

    2.1 装饰器不能修改被装饰的函数的源代码,也就是可以在不对被装饰函数做任何修改的前提下,给被装饰函数附加上一些功能。

    使用@out_func对func函数进行装饰时,我们没有对func函数的代码做什么的改变,但是被装饰后的func函数却多了开始运行和结束运行的功能。

    2.2,装饰器不能修改被装饰的函数的调用方式。在被装饰前,我们通过func调用这个函数,被装饰后,还是通过func调用这个函数。

    2.3 代码更加精简。在上面代码中,我们只是用@out_func装饰了func一个函数,但是如果有多个函数需要添加开始运行和结束运行的提示功能,如果不用装饰器,那么就需要对每一个函数进行修改,则工作量和需要修改的代码量……

    用了装饰器之后,只需要在需要添加这一功能的函数前面添加@func就可以了。

    总之:一言以盖之,装饰器可以在不改变原函数调用方式和代码情况下,为函数添加一些功能,使代码更加精简。

    我们使用装饰器来实现统计一个函数的运行时间的功能

    import time
    def timmer(f):
        def inner_func():
            start_time = time.time()
            f()
            end_time = time.time()
            print('{}函数运行消耗时间为:{}'.format(f.__name__, end_time-start_time))
        return inner_func
    
    @timmer
    def func():
        print('do_something函数运行……')
        time.sleep(1)
    
    
    if __name__ == '__main__':
        func()

    三、深入理解装饰器

    3.1 被修饰的函数带返回值

    def out_func(f):
        def inner_func():
            print('{}函数开始运行……'.format(f.__name__))
            ret = f()
            print('{}函数结束运行……'.format(f.__name__))
            return ret  # 这里返回值
        return inner_func
    
    @out_func
    def dec_dunc():
        print('正在完成功能')
        return '我是返回值'
    
    
    if __name__ == '__main__':
        ret = dec_dunc()
        print(ret)

    3.2 被装饰函数带参数

    def out_func(f):
        def inner_func(name):
            print('{}函数开始运行……'.format(f.__name__))
            ret = f(name)
            print('{}函数结束运行……'.format(f.__name__))
            return ret
        return inner_func
    
    
    @out_func
    def func(name):
        print('你好,{}!'.format(name))
        return '我是返回值'
    
    
    if __name__ == '__main__':
        ret = func('世界')
        print(ret)

     如果无法理解就理解成out_func(func)。

    3.3 装饰器函数带参数

    def key_language(key='python'):
        def out_func(f):
            def inner_func(name):
                if key == 'python':
                    print('{}函数开始运行……'.format(f.__name__))
                else:
                    print("关键字出错")
                ret = f(name)
                if key == 'python':
                    print('{}函数结束运行……'.format(f.__name__))
                else:
                    print("关键字出错")
                return ret
            return inner_func
        return out_func
    
    
    @key_language('python')
    def func(name):
        print('你好,{}!'.format(name))
        return '我是返回值'
    
    
    if __name__ == '__main__':
        ret = func('世界')
        print(ret)

    3.3 多层装饰器

    import time
    
    
    def timmer(f):
        def inner_func():
            print("timer开始运行")
            start_time = time.time()
            f()
            end_time = time.time()
            print('{}函数运行消耗时间为:{}'.format(f.__name__, end_time-start_time))
        return inner_func
    
    
    def out_func(f):
        def inner_func():
            print('{}函数开始运行……'.format(f.__name__))
            ret = f()
            print('{}函数结束运行……'.format(f.__name__))
        return inner_func
    
    
    @out_func
    @timmer
    def func():
        print("我是被装饰函数")
        time.sleep(1)
    
    
    if __name__ == '__main__':
        func()

     注意运行顺序,下面的装饰器优先于上面的装饰器运行。

    先运行f()之前的代码,下层装饰器优先运行;再运行f()代码;在运行f()之后的代码,依然是下层装饰器优先运行。

    3.4 类装饰器

    import time
    
    
    class A():
        def __init__(self, t):
            print('实例化一个A类对象……')
            self.t = t
    
        def __call__(self, f):
            def inner_A(x):
                print('延迟{}秒后开始执行……'.format(self.t))
                time.sleep(self.t)
                print('{}函数开始运行……'.format(f.__name__))
                f(x)
                print('{}函数结束运行……'.format(f.__name__))
            return inner_A
    
    
    @A(1)
    def func(name):
        print('你好,{}!'.format(name))
    
    
    if __name__ == '__main__':
        func('姚明')

    四、 内饰装饰器

    4.1 @property,@setter,@deleter

    @property装饰器所装饰的函数可以像访问属性一样调用函数,注意,@property装饰器必须先于@setter,@deleter使用,且三者说装饰的函数必须同名。

    class A():
        def __init__(self, v):
            print('实例化一个A类对象……')
            self.__value = v
    
        @property
        def value(self):
            print('取值时被调用')
            return self.__value
    
        @value.setter
        def value(self, value):
            print('赋值时被调用')
            self.__value = value
    
        @value.deleter
        def value(self):
            print('删除值时被调用……')
            del self.__value
    
    
    if __name__ == '__main__':
        a = A(123)
        print('-------------')
        print('__value的值为:{}'.format(a.value))
        print('-------------')
        a.value = 234
        print('__value的值为:{}'.format(a.value))
        print('--------------')
        del a.value
        # print('__value的值为:{}'.format(a.value))  # 执行会报错

  • 相关阅读:
    How to Use HyperV Snapshot Revert, Apply, and Delete Options
    读取含有命名空间xml文件内容
    C# 如何判断文件属性(like: readonly)
    Winform: how to cancel the winform close when click the "x" button
    Android 程序打包及签名 【图解】
    【转】近两日极为纠结——游戏服务器,选择何种技术方案?(Python + SWIGRakNet + Lua)
    apk打包和反编译三、有关 Conversion to Dalvik format failed with error 1
    (转)投票软件识别验证码思路及方法
    如何解决Android中输入法挡住输入框的问题
    A folder failed to be renamed or moved安装Android SDK的问题
  • 原文地址:https://www.cnblogs.com/qianslup/p/12193073.html
Copyright © 2011-2022 走看看