我想在调用某些函数之前定义一些通用装饰器来检查参数。
类似的东西:
@checkArguments(types = ['int', 'float'])
def myFunction(thisVarIsAnInt, thisVarIsAFloat)
''' Here my code '''
pass
附注:
答案 0 :(得分:29)
来自Decorators for Functions and Methods:
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
用法:
@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'>
答案 1 :(得分:15)
在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')"
您可以使用函数而不是lambda来获取断言失败中的名称。
答案 2 :(得分:9)
正如你当然知道的那样,仅基于其类型拒绝论证并不是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'
我使用这个技巧(使用适当的转换方法)来处理vectors 我写的许多方法都期望MyVector类具有很多功能;但有时你只想写
transpose ((2,4))
答案 3 :(得分: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
答案 4 :(得分:2)
包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
答案 5 :(得分:1)
所有这些帖子似乎都过时了 - 品脱现在提供了内置的此功能。请参阅here。在这里复制后代:
检查维度如果要将品脱数量用作 输入到您的函数,pint提供了一个包装器来确保单元 正确的类型 - 或者更准确地说,它们符合预期 物理量的维数。
与wraps()类似,您可以传递None以跳过对某些内容的检查 参数,但不检查返回参数类型。
>>> mypp = ureg.check('[length]')(pendulum_period)
以装饰器格式:
>>> @ureg.check('[length]') ... def pendulum_period(length): ... return 2*math.pi*math.sqrt(length/G)
答案 6 :(得分:0)
我有一个稍微改进的@jbouwmans sollution版本,使用python decorator模块,它使装饰器完全透明,不仅保留了签名,还保留了文档字符串,可能是使用装饰器的最优雅方式
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
答案 7 :(得分:0)
我认为这个问题的Python 3.5答案是beartype。正如本post中所解释的那样,它具有便利的功能。您的代码将如下所示
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'>
答案 8 :(得分:0)
def decorator(function):
def validation(*args):
if type(args[0]) == int and \
type(args[1]) == float:
return function(*args)
else:
print('Not valid !')
return validation
答案 9 :(得分:0)
您可以尝试使用 pydantic validation_decorator。来自文档pydantic:
<块引用>使用 python 类型注释的数据验证和设置管理。 pydantic 在运行时强制执行类型提示,并提供用户友好的 数据无效时出错。 在 benchmarks 中,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)
"""