Python学習の---pythonパラメータタイプ注記

35394 ワード

pythonパラメータタイプ注記
  • Pythonは動的言語であり、変数は随時付与可能であり、異なるタイプの
  • に付与可能である.
  • Pythonは静的コンパイル言語ではなく、変数タイプは実行期間によって決定される
  • である.
  • ダイナミック言語は柔軟ですが、この特性も弊害
  • です.
    def add(x, y): 
        return x + y
    print(add(4, 5))
    print(add('hello', 'world'))
    add(4, 'hello') #int         ,  
    
  • は発見しにくい:いかなるタイプの検査をしないため、運行期間の問題が現れるまで、あるいはオンライン運行時に問題を暴露することができる
  • .
  • 使いにくい:関数の使用者が関数を見たとき、あなたの関数の設計を知らないで、どんなタイプのデータを伝えるべきか分からない
  • このような動的言語定義の弊害をどのように解決するか.
  • ドキュメントDocumentation Stringを追加
  • これは単なる慣例であり、強制基準ではなく、プログラマに関数の説明ドキュメント
  • を提供するように要求することはできません.
  • 関数定義が更新され、ドキュメントが必ずしも同期更新されるとは限らない

  • def add(x:int,y:int):#         ,       
        """ 
        x:int
        y:int
        return:int
        """  #                     ,         
        return x+y
    
  • 関数注記
  • Python 3.5導入
  • 関数のパラメータのタイプ注記
  • 関数の戻り値のタイプ注記
  • は、関数パラメータのみを補助的に説明し、関数パラメータのタイプチェック
  • は行わない.
  • はサードパーティのツールに提供し、コード分析を行い、隠されたバグ
  • を発見した.
  • 関数注記の情報はannotationsプロパティに
  • 保存されます.

    変数注記
  • Python 3.6導入.ただし、i:int=3
  • を強制しない変数の説明にすぎません.
    ビジネスアプリケーション
  • 関数パラメータタイプチェック
  • 考え方
  • 関数パラメータの検査は、必ず関数の外にあり、検査コードを関数に侵入する
  • である.
  • 関数はパラメータとして検査関数
  • に入力されるべきである.
  • 検査関数は、関数が入力した実際のパラメータを取得し、パラメータ宣言と比較して
  • を取得する.
  • **__annotations__**属性は、戻り値タイプの宣言を含む辞書です.位置パラメータの判断を行うには、辞書の宣言に対応できないと仮定します.i nspectモジュール
  • を使用
  • inspetモジュール
  • は、オブジェクト情報を取得する関数を提供し、関数およびクラス、タイプチェック
  • を検査することができる.

    inspectモジュール
  • signature(callable)は、署名(関数署名は、関数名、そのパラメータタイプ、その存在するクラスおよび名前空間、およびその他の情報を含む関数の情報を含む)
  • を取得する.
    import inspect
    inspect.isfunction(int)
    inspect.signature(add)#              ,  return 
    def add(x:int,y:int,*args,**kwargs)-->int:
        return x+y
    sig = inspect.signature(add)#              ,  return 
    print(sig,type(sig))
    >>>(x:int, y:int, *args, **kwargs) -> int    <class 'inspect.Signature'> 
    #  sig      
    sig.parameters
    >>>mappingproxy({'x': <Parameter "x:int">,
                  'y': <Parameter "y:int">,
                  'args': <Parameter "*args">,
                  'kwargs': <Parameter "**kwargs">})  #          ,           
    sig.return_annotation #       ,       ,    inspect_empty
    >>>int                #    inspect._empty  
    print(sig.parameters['y'], type(sig.parameters['y']))
    >>>  y:int      <class 'inspect.Parameter'>  # y       int  
    print(sig.parameters['kwargs'],sig.parameters['kwargs'].annotation)
    >>>  **kwargs  <class 'inspect._empty'>  #     y   ,         ,        ,  
    

    操作
    説明
    inspect.ismethod(add))
    クラスのメソッドかどうか
    inspect.isgenerator(add))
    ジェネレータオブジェクトかどうか
    inspect.isgeneratorfunction(add))
    ジェネレータ関数かどうか
    inspect.isclass(add))
    クラスかどうか
    inspect.ismodule(inspect))
    モジュールかどうか
    inspect.isbuiltin(print))
    組み込みオブジェクトかどうか
    inspect.isfunction(add)
    関数かどうかinspect関数もたくさんありますが、必要に応じてinspectモジュールのヘルプを参照してください.
  • Parameterオブジェクト
  • はメタグループに保存され、読み取り専用の
  • である.
  • name、パラメータの名前
  • annotation、パラメータの注記、
  • が定義されていない可能性があります
  • default、パラメータのデフォルト値、
  • が定義されていない可能性があります.
  • empty、default属性または注釈annotation属性をマークするための特殊なクラス
  • kind,実パラメータがどのようにパラメータにバインドされるかは,パラメータのタイプである.
  • POSITIONAL_ONLY、値は位置パラメータ提供#pythonにposition_が存在しない必要がありますonly
  • POSITIONAL_OR_KEYWORD、値はキーワードまたは位置パラメータとして
  • を提供することができる.
  • VAR_POSITIONAL、可変位置パラメータ、対応*args
  • KEYWORD_ONLY,keyword-onlyパラメータ,args以降の出現に対応する非可変キーワードパラメータ
  • VAR_KEYWORD,可変キーワードパラメータ,対応***kwargs*


  • 例:
    
    import inspect
    def add(x, y:int=7, *args, z, t=10,**kwargs) -> int: 
        return x + y
    sig = inspect.signature(add)  #       
    print(sig)
    print('params : ', sig.parameters) #                       ('x', )
    print('return : ', sig.return_annotation)  #   return     ,      empty 
    print('~~~~~~~~~~~~~~~~')
    for i, item in enumerate(sig.parameters.items()):  #     
        name, param = item   #    
    	print(i+1, name, param.annotation, param.kind, param.default)#   ,                        
        print(param.default is param.empty, end='

    '
    ) # param.empty >>> (x, y:int=7, *args, z, t=10, **kwargs) -> int params : OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y:int=7">), ('args', <Parameter "*args">), ('z', <Parameter "z">), ('t', <Parameter "t=10">), ('kwargs', <Parameter "**kwargs">)]) return : <class 'int'> ~~~~~~~~~~~~~~~~ 1 x <class 'inspect._empty'> POSITIONAL_OR_KEYWORD <class 'inspect._empty'> True 2 y <class 'int'> POSITIONAL_OR_KEYWORD 7 False 3 args <class 'inspect._empty'> VAR_POSITIONAL <class 'inspect._empty'> True 4 z <class 'inspect._empty'> KEYWORD_ONLY <class 'inspect._empty'> True 5 t <class 'inspect._empty'> KEYWORD_ONLY 10 False 6 kwargs <class 'inspect._empty'> VAR_KEYWORD <class 'inspect._empty'> True
  • には、次の
  • の関数があります.
    def add(x, y:int=7) -> int:
           return x + y
    

    ユーザー入力がパラメータ注記の要件を満たしているかどうかを確認してください.
    考え方:
  • が呼び出すと、ユーザが入力実パラメータが要求
  • に合致するか否かを判断する.
  • が呼び出されたとき、ユーザはadd関数
  • が呼び出されたと感じた.
  • ユーザーが入力したデータと宣言のタイプを比較し、一致しない場合は
  • ユーザーに提示する.
    import inspect
    def add(x, y:int=7) -> int: 
        return x + y
    def check(fn):  #d     
        def wrapper(*args, **kwargs): 
            sig = inspect.signature(fn)
            params = sig.parameters
            values = list(params.values()) #  params  value     ,           
            for x,(k,v) in zip(args,params.items()): #  zip            ,         
                if  not isinstance(x,v.annotation): #          
                    raise Exception('wrong param= {}{}'.format(k, x))
    #      for i,p in enumerate(args):   
    #         if isinstance(p, values[i].annotation): #          
    #             print('==')
    #           ,       ,        key 
            for k,v in kwargs.items(): #            
                if isinstance(v, params[k].annotation): #          
                    print('===')
            return fn(*args, **kwargs)
        return wrapper
    check(add)(20,10)
    check(add)(20,y=10)
    check(add)(x=20,y=10)
    

    考える?ビジネス要件がパラメータに注釈がある場合、実パラメータタイプと宣言の一致が要求され、宣言がない場合は比較されます.どのように実現しますか?
    import inspect
    from inspect import Parameter
    def check (fn):
    	def wrapper (*args,**kwargs):
    		sig = inspect.signature(add)
    		params = sig.parameters
    		values = list(params.values())
    		keys = list(params.keys())
    		# for i ,x in enumerate(args):  #print(check(add)(4,5))
    		# 	if  not isinstance(x,values[i].annotation):
    		# 		raise Exception ('wrong param= {}{}'.format(keys[i],x))
    		flag =True
    		for x,(k,v) in zip(args,params.items()): #  zip           
    			if v.annotation != inspect._empty and  not isinstance(x,v.annotation): 
                    #        
    				raise Exception('wrong param= {}{}'.format(k, x))
    				flag =False
    		for k,v in kwargs.items():  #  print(check(add)(4,y=5))
    			if params[k].annotation != inspect._empty and not isinstance(v,params[k].annotation):
    				raise Exception('wrong param= {}{}'.format(k, v))
    				flag =False
    		if not  flag:  #                  ,
    			pass
    			return
    		else:
    			ret =fn(*args,**kwargs)#     
    			return ret
    	return wrapper
    @check   # add = check(add)=> wrapper(4,5)  #      
    def add(x :int ,y :int=7 )->int:
    	return x+ y
    # print(check(add)(4,5))
    # print(check(add)(4,y=5))
    print(check(add)(x=4,y=5))
    

    パラメータ注記の使用は、関数の実行中の正確性を保証し、エラーが発生してシステムがクラッシュすることはありません.タイプに合わないパラメータがあれば、ログ、データベースなどにジャンプして、相応の修正を行うことができます.パラメータタイプの不一致による損失をよく解決した.