• Python装饰器


      Python装饰器的作用是使函数包装和方法包装变得更容易阅读和理解,最常见的就是@staticmethod和@classmethod,下面将从装饰器的表现形式和常用装饰器模式两方面进行描述和总结,若有不正确之处望大家指出。

    装饰器表现形式

    1. 函数装饰器

      编写自定义装饰器有许多方法,但最简单的方法是编写一个函数,返回包装原始函数调用的一个子函数

      例1:

    #coding=utf-8
    
    def debug(func):
        def wrapper(*agrs, **kwargs):
            '''包装函数内部文档''' 
            print ("[DEBUG]:enter %s()--%s" %(func.__name__, *agrs))
            return func(*agrs, **kwargs)
        return wrapper
    @debug
    def say_hello(parm): ''' 提供函数文档字符串''' print ("say_hello") if __name__ == "__main__": say_hello("Python") print ("原始函数名:%s" %(say_hello.__name__)) print ("函数文档字符串:%s" %(say_hello.__doc__))

    >>> [DEBUG]:enter say_hello()--Python
    >>> say_hello
    >>> 原始函数名:wrapper
    >>> 函数文档字符串:包装函数内部文档

      例2:

    #coding=utf-8
    
    from functools import wraps
    
    def debug(func):
        @wraps(func)
        def wrapper(*agrs, **kwargs):
            '''包装函数内部文档''' 
            print ("[DEBUG]:enter %s()--%s" %(func.__name__, *agrs))
            return func(*agrs, **kwargs)
        return wrapper
    
    @debug
    def say_hello(parm):
        ''' 提供函数文档字符串'''
        print ("say_hello")
        
    if __name__ == "__main__":
        say_hello("Python")
        print ("原始函数名:%s" %(say_hello.__name__))
        print ("函数文档字符串:%s" %(say_hello.__doc__))

    >>> [DEBUG]:enter say_hello()--Python
    >>> say_hello
    >>> 原始函数名:say_hello
    >>> 函数文档字符串: 提供函数文档字符串

    注意例1与例2的区别,也是使用装饰器的常用错误,在使用装饰器时不保存函数元数据(文档字符串和原始函数名)

    2. 类作为装饰器

      虽然装饰器几乎总是可以用函数来实现,但如果装饰器需要复杂的参数化或者依赖特定状态的话,使用自定义类进行封装可能会更好

    #coding=utf-8
    
    from functools import wraps
    
    class debug:
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *argv, **kwargv):
            '''包装函数内部文档'''
            print ("[DEBUG]:enter %s()--%s" %(self.func.__name__, *argv))
            self.func(*argv, **kwargv)
    
    def say_hello(something):
        ''' 提供函数文档字符串 '''
        print ("say_hello", something)
        
    if __name__ == "__main__":
        De = debug(say_hello)
        De("Python")
        print ("原始函数名:%s" %(say_hello.__name__))
        print ("函数文档字符串:%s" %(say_hello.__doc__))

    >>> [DEBUG]:enter say_hello()--Python
    >>> say_hello Python
    >>> 原始函数名:say_hello
    >>> 函数文档字符串: 提供函数文档字符串

    3. 参数化装饰器

      在实际代码中通常需要使用参数化的装饰器,比如次数、类型判断等,下面是一个简单的装饰器示例,给定重复次数,每次被调用时都会重复执行被装饰函数

    #coding=utf-8
    
    from functools import wraps
    
    #参数化装饰器
    def repeat(number=3):
        def debug(func):
            @wraps(func)
            def wrapper(*argv, **kwargv):
                '''包装函数内部文档'''
                for _ in range(number):
                    print ("[DUBEG]:enter %s()--%s" %(func.__name__, *argv))
                    result = func(*argv, **kwargv)
                return result
            return wrapper 
        return debug    
    
    @repeat(2)
    def say_hello(*agrv, **kwargv):
        '''提供函数文档字符串'''
        print ("say_hello")
        
    if __name__ == "__main__":  
        say_hello("Python")
        print ("原始函数名:%s" %(say_hello.__name__))
        print ("函数文档字符串:%s" %(say_hello.__doc__))

    >>> [DUBEG]:enter say_hello()--Python
    >>> say_hello
    >>> [DUBEG]:enter say_hello()--Python
    >>> say_hello
    >>> 原始函数名:say_hello
    >>> 函数文档字符串:提供函数文档字符串

    4. 装饰器装饰类

      和装饰一个函数类似,也可以写一个函数来装饰类,用来向类中添加功能,基本原则一致,装饰器是一个函数或是一个可调用对象,它接受一个类作为参数,返回一个类作为返回值

    #coding = utf-8
    
    def decoratortest(cls):
        print ("{0.__class__.__qualname__}".format(cls))
        return cls
    
    @decoratortest
    class testclass:
        def __init__(self, value):
            self.value = value
    
        def __repr__(self):
            return "{0}:88".format(self)    
    
    if __name__ == "__main__":
     
        t = testclass(88)

    常用装饰器模式

    1. 参数检查

      将函数注册到全局字典中,并将其参数和返回值保存在一个类型列表中,并对参数类型进行检测

    #coding=utf-8
    
    '''将函数注册到全局字典中,并将其参数和返回值保存在一个类型列表中'''
    
    funname = {}
    def parmcheck(in_= (type(None),), out_ =(type(None), )):
        def fun1(func):
            func_name = func.__name__
            print ("funname:%s" %(func_name))
            funname[func.__name__] = (in_, out_)
            def checkType(elements, types):
                '''用来检查参数类型的子函数'''
                if len(elements) != len(types):
                    raise TypeError("Parm count is wrong!")
                li = zip(elements, types)
                typed = enumerate(li)
                for index,couple in typed:
                    argv, intype = couple
                    if isinstance(argv, intype):
                        print ("parm(%s) and type(%s)are all right" %(argv, intype))
                        continue
                    raise TypeError("argv %d should be %s" %(argv, intype))    
                    
            def decoratorfun(*argv):
                #types = [type(i) for i in range(len(argv))]
                #checkType(argv, types)
                checkType(argv, in_)
                res = func(*argv)
                #检查输出内容
                if type(res) not in (tuple, list):
                    checkable_res = (res, )
                else:
                    checkable_res = res
                checkType(checkable_res, out_)    
            return decoratorfun
        return fun1    
                 
    @parmcheck((int,int)) 
    def meth1(a,b):
        print ("received:%d,%d" %(a, b))
    
    if __name__=="__main__":
        meth1(1,2)
        print (funname)

    >>> funname:meth1
    >>> parm(1) and type(<class 'int'>)are all right
    >>> parm(2) and type(<class 'int'>)are all right
    >>> received:1,2
    >>> parm(None) and type(<class 'NoneType'>)are all right
    >>> {'meth1': ((<class 'int'>, <class 'int'>), (<class 'NoneType'>,))}

    注意zip、enumerate、解包、isinstance方法的使用

    2. 缓存

      缓存装饰器与参数检查十分相似,它的重点是关注那些内部状态不会影响输出的函数,每组参数都可以连接到唯一的结果

    #coding = utf-8
    
    import time
    import pickle
    import hashlib
    
    #全局字典
    cache = {}
    
    def is_obsolete(entry, duration):
        print (time.time() - entry["time"])
        return time.time() - entry["time"] > duration
    
    def compute_key(func, *argv, **kwargv):
        key = pickle.dumps((func.__name__, argv, kwargv))
        return hashlib.sha1(key).hexdigest()
    
    def memoize(duration=10):
        def _memoize(func):
            def __memoize(*argv, **kwargv):
                key = compute_key(func,*argv, **kwargv)
                if ((key in cache) and not is_obsolete(cache[key], duration)):
                    print ("we got a winner")
                    return cache[key]['value']    
                result = func(*argv, **kwargv)
                cache[key]={"value":result,"time":time.time()}
                return result
            return __memoize
        return _memoize    
    
    @memoize(3)
    def fun(a,b):
        print (a+b)
        return a+b
    
    if __name__=="__main__":
        fun(2,3)
        fun(2,2)
        fun(2,3)
        print (cache)
            

    >>> 5
    >>> 4
    >>> 0.0
    >>> we got a winner
    >>> {'a99634a4e619a2ad129df1b51002a8c0cb9cca2b': {'value': 5, 'time': 1518243058.456
    >>> 425}, '99683ddc4e22fd3f37e473de5d61699a5c27c2c6': {'value': 4, 'time': 151824305
    >>> 8.456425}}

    3. 代理

      代理装饰器使用全局机制来标记和注册函数。比如一个根据当前用户来保护代码访问的安全层可以使用集中式检查器和相关的可调用对象要求的权限来访问,这一模型常用于Python Web框架中,用于定义法布类的安全性

    4. 上下文提供者

       上下文装饰器确保函数可以允许在正确的上下文中,比如临界资源的使用

    #coding=utf-8
    
    from threading import RLock
    
    lock = RLock()
    
    def synchronized(func):
        def _synchronized(*argv, **kdargv):
            lock.require()
            try:
                return func(*argv, **kdargv)
            except:
                print ("fun error!!!")
            finally:
                lock.release()
        return _synchronized    
    上下文装饰器通常会被上下文管理器with语句代替
  • 相关阅读:
    keras系列︱迁移学习:利用InceptionV3进行fine-tuning及预测、完美案例(五)
    keras系列︱人脸表情分类与识别:opencv人脸检测+Keras情绪分类(四)
    keras系列︱图像多分类训练与利用bottleneck features进行微调(三)
    keras系列︱Application中五款已训练模型、VGG16框架(Sequential式、Model式)解读(二)
    将C++资源文件读取出来
    windows驱动程序中的预处理含义
    win10网上邻居看不到别的共享电脑怎么样办
    #pragma alloc_text 与 ALLOC_PRAGMA
    IoAllocateMdl,MmProbeAndLockPages的用法
    Composer三步曲:安装、使用、发布
  • 原文地址:https://www.cnblogs.com/xiaobingqianrui/p/8435074.html
Copyright © 2020-2023  润新知