python装飾器、記述子シミュレーションソース分析

7254 ワード

概要
私pythonの理論知識は伝授レベルに達していません.文章を書く主な目的は自己総括で、すべての人の世話をすることはできません.許してください.文章の終わりに関連リンクを貼って補充することができます.
全文は3つの部分の装飾器の理論知識、装飾器の応用、装飾器の延申に分けられる.
  • 装飾理基礎:無参装飾器、有参装飾器、functiontools、装飾器チェーン
  • 装飾器進級:property、staticmethod、classmethodソースコード分析(pythonコード実装)
  • アクセサリーベース
  • 無参装飾器
  • '''
            :          
             :
        foo1 function is starting
        foo2 function is starting
    '''
    from functools import wraps
    
    
    def NoParamDec(func):
        #          ,         ,wraps               
        @wraps(func)
        def warpper(*args, **kwargs):
            print('{} function is starting'.format(func.__name__))
            return func(*args, **kwargs)
        
        return warpper
    
    
    #python      NoParamDec=NoParamDec(foo1)
    @NoParamDec
    def foo1():
        foo2()
    
    @NoParamDec
    def foo2():
        pass
    
    
    if __name__ == "__main__":
    
        foo1()
    
    
  • 有参飾器
  • '''
            :         ,              
            :
    ('a', 'b', 'c')
    -----------------------   ------------------------
    ERROS!!!!b must be  
    ERROS!!!!c must be  
    ('a', 2, ['b', 'd'])
    
        
    '''
    from functools import wraps
    from  inspect import signature
    
    
    def typeAssert(*args, **kwargs):
        deco_args = args
        deco_kwargs = kwargs
        
        def factor(func):
            #python     ,            ,         
            sig = signature(func)
            #                
            check_bind_args = sig.bind_partial(*deco_args, **deco_kwargs).arguments
            
            @wraps(func)
            def wrapper(*args, **kwargs):
                #               
                wrapper_bind_args = sig.bind(*args, **kwargs).arguments.items()
                for name, obj in wrapper_bind_args:
                    #                   
                    if not isinstance(obj, check_bind_args[name]):
                        try:
                            raise TypeError('ERROS!!!!{arg} must be {obj} '.format(**{'arg': name, 'obj': check_bind_args[name]}))
                        except Exception as e:
                            print(e)
                return func(*args, **kwargs)
            
            return wrapper
        
        return factor
    
    
    @typeAssert(str, str, str)
    def inspect_type(a, b, c):
        return (a, b, c)
    
    
    if __name__ == "__main__":
        print(inspect_type('a', 'b', 'c'))
        print('{:-^50}'.format('   '))
        print(inspect_type('a', 2, ['b', 'd']))
    
    
    
  • 装飾チェーン
  • '''
            :
          :
    @makebold
    @makeitalic
    def say():
       return "Hello"
    
      :
    Hello
    '''
    from functools import wraps
    
    
    def html_deco(tag):
        def decorator(fn):
            @wraps(fn)
            def wrapped(*args, **kwargs):
                return '{fn_result}'.format(**{'tag': tag, 'fn_result': fn(*args, **kwargs)})
            
            return wrapped
        
        return decorator
    
    
    @html_deco('b')
    @html_deco('i')
    def greet(whom=''):
        #     geet=html_deco('b')(html_deco('i)(geet))
        return 'Hello' + (' ' + whom) if whom else ''
    
    
    if __name__ == "__main__":
        print(greet('world'))  # -> Hello world
    
    
    

    デコレーションステップ
  • propertyの原理は、通常、記述子は、記述子プロトコル内のメソッドによって上書きされた「バインド動作」を有するオブジェクト属性である.これらの方法はget()、set()、delete()です.オブジェクトがこれらのメソッドのいずれかを定義する場合は、記述子と呼ばれます.オブジェクトがget()とset()を定義する場合、オブジェクトはデータ記述子とみなされます.get()のみを定義するディスクリプタは、非データディスクリプタと呼ばれます(通常はメソッドに使用されますが、他の用途も可能です).

  • 属性検索の優先順位は次のとおりです.
  • クラス属性
  • データ記述子
  • インスタンス属性
  • 非データ記述子
  • デフォルトはgetattr()
  • です.
    class Property(object):
        '''
          property  c   ,   python    property  
              doc  
        '''
    
        def __init__(self, fget=None, fset=None, fdel=None, doc=None):
            self.fget = fget
            self.fset = fset
            self.fdel = fdel
            self.__doc__ = doc
    
        def __get__(self, obj, objtype=None):
            if obj is None:
                return self
            if self.fget is None:
                raise (AttributeError, "unreadable attribute")
            print('self={},obj={},objtype={}'.format(self,obj,objtype))
            return self.fget(obj)
    
        def __set__(self, obj, value):
            if self.fset is None:
                raise (AttributeError, "can't set attribute")
            self.fset(obj, value)
    
        def __delete__(self, obj):
            if self.fdel is None:
                raise (AttributeError, "can't delete attribute")
            self.fdel(obj)
    
        def getter(self, fget):
            return type(self)(fget, self.fset, self.fdel, self.__doc__)
    
        def setter(self, fset):
            return type(self)(self.fget, fset, self.fdel, self.__doc__)
    
        def deleter(self, fdel):
            return type(self)(self.fget, self.fset, fdel, self.__doc__)
    
    
    class Student( object ):
        @Property
        def score( self ):
            return self._score
        @score.setter
        def score( self, val ):
            if not isinstance( val, int ):
                raise ValueError( 'score must be an integer!' )
            if val > 100 or val < 0:
                raise ValueError( 'score must between 0 ~ 100!' )
            self._score = val
    
    
    if __name__ == "__main__":
        s = Student()
        s.score = 60   
        s.score         
    
  • staticmethod原理@staticmethod means:when this method is called,we don't pass an instance of the class to it(as we normally do with methods).This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).
  • class StaticMethod(object):
        "python    staticmethod  "
        
        def __init__(self, f):
            self.f = f
        
        def __get__(self, obj, objtype=None):
            return self.f
    
    
    class E(object):
        #StaticMethod=StaticMethod(f)
        @StaticMethod
        def f( x):
            return x
    
    if __name__ == "__main__":
        print(E.f('staticMethod Test'))
    
    
  • classmethod @staticmethod means: when this method is called, we don't pass an instance of the class to it (as we normally do with methods). This means you can put a function inside a class but you can't access the instance of that class (this is useful when your method does not use the instance).
  • class ClassMethod(object):
        "python    classmethod  "
        
        def __init__(self, f):
            self.f = f
        
        def __get__(self, obj, klass=None):
            if klass is None:
                klass = type(obj)
            
            def newfunc(*args):
                return self.f(klass, *args)
            
            return newfunc
        
    class E(object):
        #ClassMethod=ClassMethod(f)
        @ClassMethod
        def f(cls,x):
            return x
        
    if __name__ == "__main__":
        print(E().f('classMethod Test'))
    
    

    参考資料
    1, statckoverflow: how to make a chain of decorators
    2, python doc:how to descriptor
    3、知っています:どのように装飾器を理解します
    4, difference-between-staticmethod-and-classmethod-in-python
    5,meaning-of-classmethod-and-staticmethod-for-beginner