44

いくつかの関数を呼び出す前に引数をチェックするために、いくつかの汎用デコレータを定義したいと思います。

何かのようなもの:

@checkArguments(types = ['int', 'float'])
def myFunction(thisVarIsAnInt, thisVarIsAFloat)
    ''' Here my code '''
    pass

サイドノート:

  1. タイプチェックは例を示すためにここにあります
  2. 私はPython2.7を使用していますが、Python3.0も興味深いでしょう

編集2021:型のヒントmypyを使用して、型チェックが長期的にはアンチパイソンにならなかったのはおかしいです。

4

10 に答える 10

49

関数とメソッドのデコレータから:

Python 2

def accepts(*types):
    def check_accepts(f):
        assert len(types) == f.func_code.co_argcount
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.func_name = f.func_name
        return new_f
    return check_accepts

Python 3

Python 3func_codeでは、に変更され__code__func_nameに変更されました__name__

def accepts(*types):
    def check_accepts(f):
        assert len(types) == f.__code__.co_argcount
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                assert isinstance(a, t), \
                       "arg %r does not match %s" % (a,t)
            return f(*args, **kwds)
        new_f.__name__ = f.__name__
        return new_f
    return check_accepts

使用法:

@accepts(int, (int,float))
def func(arg1, arg2):
    return arg1 * arg2

func(3, 2) # -> 6
func('3', 2) # -> AssertionError: arg '3' does not match <type 'int'>

arg2intまたは_float

于 2013-03-08T17:49:24.377 に答える
17

Python 3.3では、関数アノテーションを使用して以下を検査できます。

import inspect

def validate(f):
    def wrapper(*args):
        fname = f.__name__
        fsig = inspect.signature(f)
        vars = ', '.join('{}={}'.format(*pair) for pair in zip(fsig.parameters, args))
        params={k:v for k,v in zip(fsig.parameters, args)}
        print('wrapped call to {}({})'.format(fname, params))
        for k, v in fsig.parameters.items():
            p=params[k]
            msg='call to {}({}): {} failed {})'.format(fname, vars, k, v.annotation.__name__)
            assert v.annotation(params[k]), msg
        ret = f(*args)
        print('  returning {} with annotation: "{}"'.format(ret, fsig.return_annotation))
        return ret
    return wrapper

@validate
def xXy(x: lambda _x: 10<_x<100, y: lambda _y: isinstance(_y,float)) -> ('x times y','in X and Y units'):
    return x*y

xy = xXy(10,3)
print(xy)

検証エラーがある場合は、次のように出力します。

AssertionError: call to xXy(x=12, y=3): y failed <lambda>)

検証エラーがない場合は、次のように出力します。

wrapped call to xXy({'y': 3.0, 'x': 12})
  returning 36.0 with annotation: "('x times y', 'in X and Y units')"

ラムダではなく関数を使用して、アサーションの失敗で名前を取得できます。

于 2013-03-08T17:32:41.830 に答える
11

ご存知のように、型だけに基づいて引数を拒否するのはpythonicではありません。
Pythonicのアプローチは、むしろ「最初に対処しよう」です。
そのため、引数を変換するためにデコレータを使用したいのです。

def enforce(*types):
    def decorator(f):
        def new_f(*args, **kwds):
            #we need to convert args into something mutable   
            newargs = []        
            for (a, t) in zip(args, types):
               newargs.append( t(a)) #feel free to have more elaborated convertion
            return f(*newargs, **kwds)
        return new_f
    return decorator

このように、あなたの関数はあなたが期待するタイプで供給されますしかし、パラメータがフロートのように震えることができるなら、それは受け入れられます

@enforce(int, float)
def func(arg1, arg2):
    return arg1 * arg2

print (func(3, 2)) # -> 6.0
print (func('3', 2)) # -> 6.0
print (func('three', 2)) # -> ValueError: invalid literal for int() with base 10: 'three'

私はこのトリックを(適切な変換方法で)ベクトルを処理するために使用します。
私が書いた多くのメソッドは、MyVectorクラスがたくさんの機能を持っているので、それを期待しています。でも時々あなたはただ書きたいだけです

transpose ((2,4))
于 2016-02-29T14:58:24.507 に答える
5

パッケージtypeguardはこのためのデコレータを提供し、型アノテーションから型情報を読み取りますが、Python>=3.5.2が必要です。結果のコードはかなりいいと思います。

@typeguard.typechecked
def my_function(this_var_is_an_int: int, this_var_is_a_float: float)
    ''' Here my code '''
    pass
于 2020-01-13T20:02:44.403 に答える
2

文字列以外の入力が提供されたときに不可解なエラーをスローするパーサーに文字列引数を適用するために、割り当てと関数呼び出しを回避しようとする次のように記述しました。

from functools import wraps

def argtype(**decls):
    """Decorator to check argument types.

    Usage:

    @argtype(name=str, text=str)
    def parse_rule(name, text): ...
    """

    def decorator(func):
        code = func.func_code
        fname = func.func_name
        names = code.co_varnames[:code.co_argcount]

        @wraps(func)
        def decorated(*args,**kwargs):
            for argname, argtype in decls.iteritems():
                try:
                    argval = args[names.index(argname)]
                except ValueError:
                    argval = kwargs.get(argname)
                if argval is None:
                    raise TypeError("%s(...): arg '%s' is null"
                                    % (fname, argname))
                if not isinstance(argval, argtype):
                    raise TypeError("%s(...): arg '%s': type is %s, must be %s"
                                    % (fname, argname, type(argval), argtype))
            return func(*args,**kwargs)
        return decorated

    return decorator
于 2013-03-22T18:19:46.583 に答える
1

pythonデコレータモジュールを使用して、@ jbouwmansソリューションのバージョンを少し改善しました。これにより、デコレータが完全に透明になり、署名だけでなくdocstringも所定の位置に保持され、デコレータを使用する最もエレガントな方法になる可能性があります。

from decorator import decorator

def check_args(**decls):
    """Decorator to check argument types.

    Usage:

    @check_args(name=str, text=str)
    def parse_rule(name, text): ...
    """
    @decorator
    def wrapper(func, *args, **kwargs):
        code = func.func_code
        fname = func.func_name
        names = code.co_varnames[:code.co_argcount]
        for argname, argtype in decls.iteritems():
            try:
                argval = args[names.index(argname)]
            except IndexError:
                argval = kwargs.get(argname)
            if argval is None:
                raise TypeError("%s(...): arg '%s' is null"
                            % (fname, argname))
            if not isinstance(argval, argtype):
                raise TypeError("%s(...): arg '%s': type is %s, must be %s"
                            % (fname, argname, type(argval), argtype))
    return func(*args, **kwargs)
return wrapper
于 2015-06-08T13:16:32.397 に答える
1

この質問に対するPython3.5の答えはbeartypeだと思います。この投稿で説明されているように、便利な機能が付属しています。コードは次のようになります

from beartype import beartype
@beartype
def sprint(s: str) -> None:
   print(s)

と結果

>>> sprint("s")
s
>>> sprint(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 13, in func_beartyped
TypeError: sprint() parameter s=3 not of <class 'str'>
于 2016-08-25T12:54:45.847 に答える
1

これらの投稿はすべて古くなっているようです。pintはこの機能を組み込みで提供するようになりました。こちらをご覧ください。後世のためにここにコピーされました:

次元の確認パイント量を関数への入力として使用する場合、pintは、単位が正しいタイプであることを確認するためのラッパーを提供します。より正確には、物理​​量の予想される次元と一致します。

wraps()と同様に、Noneを渡して一部のパラメーターのチェックをスキップできますが、戻りパラメーターのタイプはチェックされません。

>>> mypp = ureg.check('[length]')(pendulum_period) 

デコレータ形式の場合:

>>> @ureg.check('[length]')
... def pendulum_period(length):
...     return 2*math.pi*math.sqrt(length/G)
于 2018-04-21T18:46:15.540 に答える
0

pydanticvalidation_decoratorで試すことができます。ドキュメントpydanticから:

Pythonタイプのアノテーションを使用したデータ検証と設定管理。pydanticは実行時に型ヒントを適用し、データが無効な場合にユーザーフレンドリーなエラーを提供します。ベンチマークでは、pydanticは他のすべてのテスト済みライブラリよりも高速です。

from pydantic import validate_arguments, ValidationError


@validate_arguments
def repeat(s: str, count: int, *, separator: bytes = b'') -> bytes:
    b = s.encode()
    return separator.join(b for _ in range(count))


a = repeat('hello', 3)
print(a)
#> b'hellohellohello'

b = repeat('x', '4', separator=' ')
print(b)
#> b'x x x x'

try:
    c = repeat('hello', 'wrong')
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Repeat
    count
      value is not a valid integer (type=type_error.integer)
    """
于 2021-01-03T05:16:10.057 に答える
0

私にとって、上記で共有されたコードは複雑に見えます。タイプチェック用の「ジェネリックデコレータ」を定義するために私がしたこと:

* args、** kwargs機能を使用しました。関数/メソッドを使用する場合は少し余分な作業が必要ですが、管理は簡単です。

テストの適切な定義例

argument_types = {
'name':str,
'count':int,
'value':float
}

装飾の定義

//from functools import wraps

def azure_type(func):
    @wraps(func)
    def type_decorator(*args, **kwargs):
        for key, value in kwargs.items():
            if key in argument_types:
                if type(value) != argument_types[key]:
                    #enter code here
                    return 'Error Message or what ever you like to do'  
        return func(*args, **kwargs)
    return type_decorator 

コードの簡単なサンプル

// all other definitions

@azure_type
def stt(name:str, value:float)->(int):
    #some calculation and creation of int output 
    count_output = #something int
    return count_output

// call the function:

stt(name='ati', value=32.90) #can test from that

于 2021-10-20T14:05:38.440 に答える