我正在寻找一种检查python函数变量的有效方法。例如,我想检查参数类型和值。有这个模块吗?或者我应该使用类似装饰器或任何特定习语的东西?
def my_function(a, b, c):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
答案 0 :(得分:65)
在这个细长的答案中,我们实现了一个基于PEP 484的特定于Python 3.x的类型检查装饰器 - 样式类型提示少于275行纯Python(大多数是解释性文档字符串和注释) - 针对工业强度的实际使用进行了大量优化,并配有py.test
驱动的测试套件,可以处理所有可能的边缘情况。
非常棒的熊打字:
>>> @beartype
... def spirit_bear(kermode: str, gitgaata: (str, int)) -> tuple:
... return (kermode, gitgaata, "Moksgm'ol", 'Ursus americanus kermodei')
>>> spirit_bear(0xdeadbeef, 'People of the Cane')
AssertionError: parameter kermode=0xdeadbeef not of <class "str">
正如此示例所示,熊类型显式支持对参数进行类型检查,并返回注释为此类型的简单类型或元组的值。 天哪!
O.K。,这实际上并不那么令人印象深刻。 @beartype
类似于基于PEP 484的每个其他 Python 3.x特定类型检查装饰器 - 样式类型提示少于275行纯Python。那么摩擦是什么呢?
在我的有限领域知识中,熊类型在空间和时间方面都比Python中所有现有的类型检查实现更有效。 (稍后会详细介绍。)
然而,效率通常在Python中并不重要。如果是这样,你就不会使用Python。类型检查是否实际上偏离了避免在Python中过早优化的既定规范? 是。是的,确实如此。考虑分析,这会为每个感兴趣的分析指标(例如,函数调用,行)增加不可避免的开销。为了确保准确的结果,通过利用优化的C扩展(例如,_lsprof
模块利用的cProfile
C扩展)而不是未优化的纯Python(例如profile
)来减轻此开销。模块)。在分析时,效率确实确实。
类型检查也不例外。类型检查会为应用程序检查的每个函数调用类型增加开销 - 理想情况下,所有。为了防止善意(但可悲的是心胸狭隘)的同事在上周五将咖啡因添加的明星添加到您的老年人遗留Django网络应用程序之后删除您默默添加的类型检查,类型检查必须快速。< / strong>当你在不告诉任何人的情况下添加它时,没有人注意到它就在那里。 我一直这样做!如果你是同事,请停止阅读。
但是,如果即使是荒谬的速度对于你的贪吃应用程序来说还不够,那么通过启用Python优化(例如,通过将-O
选项传递给Python解释器)可以全局禁用熊类型:
$ python3 -O
# This succeeds only when type checking is optimized away. See above!
>>> spirit_bear(0xdeadbeef, 'People of the Cane')
(0xdeadbeef, 'People of the Cane', "Moksgm'ol", 'Ursus americanus kermodei')
只是因为。欢迎来打字。
熊类型是裸机类型检查 - 也就是说,类型检查尽可能接近Python中类型检查的手动方法。熊类型旨在强加 no 性能惩罚,兼容性约束或第三方依赖(无论如何都超过了手动方法所强加的)。熊类型可以无缝地集成到现有的代码库和测试套件中而无需修改。
每个人都可能熟悉手动方法。您手动assert
传递给每个参数和/或返回从代码库中每个函数返回的值。什么样板可能更简单或更平庸?我们在googleplex时代都看过它一百次,每当我们做的时候,我们都会呕吐一点。重复变老了。 DRY,哟。
准备好你的呕吐袋。为简洁起见,我们假设一个简化的easy_spirit_bear()
函数只接受一个str
参数。这是手动方法的样子:
def easy_spirit_bear(kermode: str) -> str:
assert isinstance(kermode, str), 'easy_spirit_bear() parameter kermode={} not of <class "str">'.format(kermode)
return_value = (kermode, "Moksgm'ol", 'Ursus americanus kermodei')
assert isinstance(return_value, str), 'easy_spirit_bear() return value {} not of <class "str">'.format(return_value)
return return_value
Python 101,对吗?我们中的许多人都通过了这门课程。
熊类型将上述方法手动执行的类型检查提取到动态定义的包装函数中,自动执行相同的检查 - 还有一个额外的好处,即提高粒度TypeError
而不是模糊的AssertionError
异常。这是自动化方法的样子:
def easy_spirit_bear_wrapper(*args, __beartype_func=easy_spirit_bear, **kwargs):
if not (
isinstance(args[0], __beartype_func.__annotations__['kermode'])
if 0 < len(args) else
isinstance(kwargs['kermode'], __beartype_func.__annotations__['kermode'])
if 'kermode' in kwargs else True):
raise TypeError(
'easy_spirit_bear() parameter kermode={} not of {!r}'.format(
args[0] if 0 < len(args) else kwargs['kermode'],
__beartype_func.__annotations__['kermode']))
return_value = __beartype_func(*args, **kwargs)
if not isinstance(return_value, __beartype_func.__annotations__['return']):
raise TypeError(
'easy_spirit_bear() return value {} not of {!r}'.format(
return_value, __beartype_func.__annotations__['return']))
return return_value
啰嗦。但它也基本上与手动方法一样快 * 。 * Squinting建议。
请注意,包装函数中完全没有函数检查或迭代,其中包含与原始函数类似的测试数量 - 尽管测试是否以及如何检查参数的额外(可忽略不计)成本是传递给当前函数调用。你无法赢得每场战斗。
可以可靠地生成实际这样的包装函数,以便在少于275行的纯Python中键入检查任意函数吗? Snake Plisskin说,&#34;真实的故事。抽了烟?&#34;
而且,是的。我可能有一个领带。
熊击败鸭子。鸭子可能会飞,但熊可能会把鸭子扔到鸭子身上。 在加拿大,大自然会让你感到惊讶。
下一个问题。
现有的解决方案不执行裸机类型检查 - 至少,我没有抓过。他们都在每个函数调用上迭代地重新检查类型检查函数的签名。虽然对于单个呼叫可忽略不计,但在通过所有呼叫进行聚合时,重新检查开销通常是不可忽略的。 真的,真的不可忽视。
然而,这不仅仅是效率问题。现有解决方案通常也无法解决常见的边缘情况。这包括大多数(如果不是全部)玩具装饰器在此处和其他地方作为stackoverflow答案提供。经典失败包括:@checkargs
decorator)。isinstance()
内置接受的类型的元组(即工会)。AssertionError
例外而非特定TypeError
例外。对于粒度和健全性,类型检查应该从不引发一般异常。在非熊失败的地方,熊的打字成功。所有人都忍受了!
熊类型将检查函数签名的空间和时间成本从函数调用时间转移到函数定义时间 - 也就是说,从@beartype
装饰器返回到装饰器本身的包装函数。由于每个函数定义只调用一次装饰器,因此这种优化会为所有人带来欢乐。
@beartype
:
exec()
builtin。我们好吗?让我们深入探讨。
# If the active Python interpreter is *NOT* optimized (e.g., option "-O" was
# *NOT* passed to this interpreter), enable type checking.
if __debug__:
import inspect
from functools import wraps
from inspect import Parameter, Signature
def beartype(func: callable) -> callable:
'''
Decorate the passed **callable** (e.g., function, method) to validate
both all annotated parameters passed to this callable _and_ the
annotated value returned by this callable if any.
This decorator performs rudimentary type checking based on Python 3.x
function annotations, as officially documented by PEP 484 ("Type
Hints"). While PEP 484 supports arbitrarily complex type composition,
this decorator requires _all_ parameter and return value annotations to
be either:
* Classes (e.g., `int`, `OrderedDict`).
* Tuples of classes (e.g., `(int, OrderedDict)`).
If optimizations are enabled by the active Python interpreter (e.g., due
to option `-O` passed to this interpreter), this decorator is a noop.
Raises
----------
NameError
If any parameter has the reserved name `__beartype_func`.
TypeError
If either:
* Any parameter or return value annotation is neither:
* A type.
* A tuple of types.
* The kind of any parameter is unrecognized. This should _never_
happen, assuming no significant changes to Python semantics.
'''
# Raw string of Python statements comprising the body of this wrapper,
# including (in order):
#
# * A "@wraps" decorator propagating the name, docstring, and other
# identifying metadata of the original function to this wrapper.
# * A private "__beartype_func" parameter initialized to this function.
# In theory, the "func" parameter passed to this decorator should be
# accessible as a closure-style local in this wrapper. For unknown
# reasons (presumably, a subtle bug in the exec() builtin), this is
# not the case. Instead, a closure-style local must be simulated by
# passing the "func" parameter to this function at function
# definition time as the default value of an arbitrary parameter. To
# ensure this default is *NOT* overwritten by a function accepting a
# parameter of the same name, this edge case is tested for below.
# * Assert statements type checking parameters passed to this callable.
# * A call to this callable.
# * An assert statement type checking the value returned by this
# callable.
#
# While there exist numerous alternatives (e.g., appending to a list or
# bytearray before joining the elements of that iterable into a string),
# these alternatives are either slower (as in the case of a list, due to
# the high up-front cost of list construction) or substantially more
# cumbersome (as in the case of a bytearray). Since string concatenation
# is heavily optimized by the official CPython interpreter, the simplest
# approach is (curiously) the most ideal.
func_body = '''
@wraps(__beartype_func)
def func_beartyped(*args, __beartype_func=__beartype_func, **kwargs):
'''
# "inspect.Signature" instance encapsulating this callable's signature.
func_sig = inspect.signature(func)
# Human-readable name of this function for use in exceptions.
func_name = func.__name__ + '()'
# For the name of each parameter passed to this callable and the
# "inspect.Parameter" instance encapsulating this parameter (in the
# passed order)...
for func_arg_index, func_arg in enumerate(func_sig.parameters.values()):
# If this callable redefines a parameter initialized to a default
# value by this wrapper, raise an exception. Permitting this
# unlikely edge case would permit unsuspecting users to
# "accidentally" override these defaults.
if func_arg.name == '__beartype_func':
raise NameError(
'Parameter {} reserved for use by @beartype.'.format(
func_arg.name))
# If this parameter is both annotated and non-ignorable for purposes
# of type checking, type check this parameter.
if (func_arg.annotation is not Parameter.empty and
func_arg.kind not in _PARAMETER_KIND_IGNORED):
# Validate this annotation.
_check_type_annotation(
annotation=func_arg.annotation,
label='{} parameter {} type'.format(
func_name, func_arg.name))
# String evaluating to this parameter's annotated type.
func_arg_type_expr = (
'__beartype_func.__annotations__[{!r}]'.format(
func_arg.name))
# String evaluating to this parameter's current value when
# passed as a keyword.
func_arg_value_key_expr = 'kwargs[{!r}]'.format(func_arg.name)
# If this parameter is keyword-only, type check this parameter
# only by lookup in the variadic "**kwargs" dictionary.
if func_arg.kind is Parameter.KEYWORD_ONLY:
func_body += '''
if {arg_name!r} in kwargs and not isinstance(
{arg_value_key_expr}, {arg_type_expr}):
raise TypeError(
'{func_name} keyword-only parameter '
'{arg_name}={{}} not a {{!r}}'.format(
{arg_value_key_expr}, {arg_type_expr}))
'''.format(
func_name=func_name,
arg_name=func_arg.name,
arg_type_expr=func_arg_type_expr,
arg_value_key_expr=func_arg_value_key_expr,
)
# Else, this parameter may be passed either positionally or as
# a keyword. Type check this parameter both by lookup in the
# variadic "**kwargs" dictionary *AND* by index into the
# variadic "*args" tuple.
else:
# String evaluating to this parameter's current value when
# passed positionally.
func_arg_value_pos_expr = 'args[{!r}]'.format(
func_arg_index)
func_body += '''
if not (
isinstance({arg_value_pos_expr}, {arg_type_expr})
if {arg_index} < len(args) else
isinstance({arg_value_key_expr}, {arg_type_expr})
if {arg_name!r} in kwargs else True):
raise TypeError(
'{func_name} parameter {arg_name}={{}} not of {{!r}}'.format(
{arg_value_pos_expr} if {arg_index} < len(args) else {arg_value_key_expr},
{arg_type_expr}))
'''.format(
func_name=func_name,
arg_name=func_arg.name,
arg_index=func_arg_index,
arg_type_expr=func_arg_type_expr,
arg_value_key_expr=func_arg_value_key_expr,
arg_value_pos_expr=func_arg_value_pos_expr,
)
# If this callable's return value is both annotated and non-ignorable
# for purposes of type checking, type check this value.
if func_sig.return_annotation not in _RETURN_ANNOTATION_IGNORED:
# Validate this annotation.
_check_type_annotation(
annotation=func_sig.return_annotation,
label='{} return type'.format(func_name))
# Strings evaluating to this parameter's annotated type and
# currently passed value, as above.
func_return_type_expr = (
"__beartype_func.__annotations__['return']")
# Call this callable, type check the returned value, and return this
# value from this wrapper.
func_body += '''
return_value = __beartype_func(*args, **kwargs)
if not isinstance(return_value, {return_type}):
raise TypeError(
'{func_name} return value {{}} not of {{!r}}'.format(
return_value, {return_type}))
return return_value
'''.format(func_name=func_name, return_type=func_return_type_expr)
# Else, call this callable and return this value from this wrapper.
else:
func_body += '''
return __beartype_func(*args, **kwargs)
'''
# Dictionary mapping from local attribute name to value. For efficiency,
# only those local attributes explicitly required in the body of this
# wrapper are copied from the current namespace. (See below.)
local_attrs = {'__beartype_func': func}
# Dynamically define this wrapper as a closure of this decorator. For
# obscure and presumably uninteresting reasons, Python fails to locally
# declare this closure when the locals() dictionary is passed; to
# capture this closure, a local dictionary must be passed instead.
exec(func_body, globals(), local_attrs)
# Return this wrapper.
return local_attrs['func_beartyped']
_PARAMETER_KIND_IGNORED = {
Parameter.POSITIONAL_ONLY, Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD,
}
'''
Set of all `inspect.Parameter.kind` constants to be ignored during
annotation- based type checking in the `@beartype` decorator.
This includes:
* Constants specific to variadic parameters (e.g., `*args`, `**kwargs`).
Variadic parameters cannot be annotated and hence cannot be type checked.
* Constants specific to positional-only parameters, which apply to non-pure-
Python callables (e.g., defined by C extensions). The `@beartype`
decorator applies _only_ to pure-Python callables, which provide no
syntactic means of specifying positional-only parameters.
'''
_RETURN_ANNOTATION_IGNORED = {Signature.empty, None}
'''
Set of all annotations for return values to be ignored during annotation-
based type checking in the `@beartype` decorator.
This includes:
* `Signature.empty`, signifying a callable whose return value is _not_
annotated.
* `None`, signifying a callable returning no value. By convention, callables
returning no value are typically annotated to return `None`. Technically,
callables whose return values are annotated as `None` _could_ be
explicitly checked to return `None` rather than a none-`None` value. Since
return values are safely ignorable by callers, however, there appears to
be little real-world utility in enforcing this constraint.
'''
def _check_type_annotation(annotation: object, label: str) -> None:
'''
Validate the passed annotation to be a valid type supported by the
`@beartype` decorator.
Parameters
----------
annotation : object
Annotation to be validated.
label : str
Human-readable label describing this annotation, interpolated into
exceptions raised by this function.
Raises
----------
TypeError
If this annotation is neither a new-style class nor a tuple of
new-style classes.
'''
# If this annotation is a tuple, raise an exception if any member of
# this tuple is not a new-style class. Note that the "__name__"
# attribute tested below is not defined by old-style classes and hence
# serves as a helpful means of identifying new-style classes.
if isinstance(annotation, tuple):
for member in annotation:
if not (
isinstance(member, type) and hasattr(member, '__name__')):
raise TypeError(
'{} tuple member {} not a new-style class'.format(
label, member))
# Else if this annotation is not a new-style class, raise an exception.
elif not (
isinstance(annotation, type) and hasattr(annotation, '__name__')):
raise TypeError(
'{} {} neither a new-style class nor '
'tuple of such classes'.format(label, annotation))
# Else, the active Python interpreter is optimized. In this case, disable type
# checking by reducing this decorator to the identity decorator.
else:
def beartype(func: callable) -> callable:
return func
并且leycec说,让@beartype
快速进行类型检查:就是这样。
没有什么是完美的。 即使是小熊打字。
熊输入不类型检查未分配参数分配的默认值。从理论上讲,它可以。但不是275行或更少,当然不是作为stackoverflow的答案。
安全(... 可能完全不安全)假设是功能实现者声称他们在定义默认值时知道他们在做什么。由于默认值通常是常量(... 它们最好是),因此重新检查在每个函数调用中从不更改的常量类型分配一个或多个默认值将违反基本原则熊打字:&#34;不要重复自己,再次 oooover 和 oooo-oooover 。&#34;
告诉我错了,我会用支持给你洗澡。
PEP 484(&#34; Type Hints&#34; )形式化了PEP 3107首先引入的函数注释的使用(&#34;功能注释& #34; )。 Python 3.5通过新的顶级typing
module表面支持这种形式化,这是一种标准API,用于从更简单的类型组合任意复杂类型(例如,Callable[[Arg1Type, Arg2Type], ReturnType]
,一种描述接受类型{{的两个参数的函数的类型1}}和Arg1Type
并返回Arg2Type
类型的值。
熊打字不支持任何一个。从理论上讲,它可以。但不是275行或更少,当然不是作为stackoverflow的答案。
然而,熊类型确实支持类型的联合,就像ReturnType
内置支持类型的联合一样:作为元组。这表面对应于isinstance()
类型 - 明显需要注意typing.Union
支持任意复杂类型,而typing.Union
接受的元组仅支持 简单类。在我的辩护中,275行。
这是gist的py.test
。 获取它, gist ?我现在就停下来。
与@beartype
装饰器本身一样,这些mypy测试可以无需修改即可无缝集成到现有测试套件中。珍贵,不是吗?
现在强制性的脖子咆哮没有人要求。
Python 3.5没有为使用PEP 484类型提供实际支持。 笏?
这是真的:没有类型检查,没有类型推断,没有类型nuthin&#39;。相反,开发人员应该通过重量级的第三方CPython解释器包装器来定期运行他们的整个代码库,实现这种支持的传真(例如,official mypy FAQ)。当然,这些包装纸强加:
我问圭多:&#34;为什么?如果你不愿意用一个具体的API来实现这个抽象的东西,为什么还要发明一个抽象的API?&#34;为什么要把一百万Pythonistas的命运留给自由开源市场的关节炎手?为什么要在官方Python stdlib中使用275行装饰器创建另一个技术问题呢?
我没有Python,我必须尖叫。
答案 1 :(得分:58)
最Pythonic的成语是明确地记录函数所期望的内容,然后尝试使用传递给你的函数的任何东西,让异常传播或者只捕获属性错误并引发{{1相反。应尽可能避免类型检查,因为它违反鸭子类型。价值测试可以没问题 - 取决于具体情况。
验证真正有意义的唯一地方是系统或子系统入口点,例如Web表单,命令行参数等。在其他地方,只要您的函数被正确记录,调用者就有责任传递适当的参数
答案 2 :(得分:17)
类型检查通常不是Pythonic。在Python中,更常见的是使用duck typing。例如:
在您的代码中,假设参数(在您的示例中为a
)像int
一样行走,并像int
那样嘎嘎作响。例如:
def my_function(a):
return a + 7
这意味着您的函数不仅可以使用整数,还可以使用浮点数和任何定义了__add__
方法的用户定义类,因此如果您或某人需要更少(有时没有)否则,希望扩展您的功能以使用其他东西。但是,在某些情况下,您可能需要int
,因此您可以执行以下操作:
def my_function(a):
b = int(a) + 7
c = (5, 6, 3, 123541)[b]
return c
并且该函数仍适用于定义a
方法的任何__int__
。
在回答你的其他问题时,我认为这是最好的(正如其他答案所说的那样:
def my_function(a, b, c):
assert 0 < b < 10
assert c # A non-empty string has the Boolean value True
或
def my_function(a, b, c):
if 0 < b < 10:
# Do stuff with b
else:
raise ValueError
if c:
# Do stuff with c
else:
raise ValueError
我制作的一些类型检查装饰器:
import inspect
def checkargs(function):
def _f(*arguments):
for index, argument in enumerate(inspect.getfullargspec(function)[0]):
if not isinstance(arguments[index], function.__annotations__[argument]):
raise TypeError("{} is not of type {}".format(arguments[index], function.__annotations__[argument]))
return function(*arguments)
_f.__doc__ = function.__doc__
return _f
def coerceargs(function):
def _f(*arguments):
new_arguments = []
for index, argument in enumerate(inspect.getfullargspec(function)[0]):
new_arguments.append(function.__annotations__[argument](arguments[index]))
return function(*new_arguments)
_f.__doc__ = function.__doc__
return _f
if __name__ == "__main__":
@checkargs
def f(x: int, y: int):
"""
A doc string!
"""
return x, y
@coerceargs
def g(a: int, b: int):
"""
Another doc string!
"""
return a + b
print(f(1, 2))
try:
print(f(3, 4.0))
except TypeError as e:
print(e)
print(g(1, 2))
print(g(3, 4.0))
答案 3 :(得分:11)
一种方法是使用assert
:
def myFunction(a,b,c):
"This is an example function I'd like to check arguments of"
assert isinstance(a, int), 'a should be an int'
# or if you want to allow whole number floats: assert int(a) == a
assert b > 0 and b < 10, 'b should be betwen 0 and 10'
assert isinstance(c, str) and c, 'c should be a non-empty string'
答案 4 :(得分:6)
您可以使用Type Enforcement接受/返回装饰器 PythonDecoratorLibrary 它非常简单易读:
@accepts(int, int, float)
def myfunc(i1, i2, i3):
pass
答案 5 :(得分:5)
有多种方法可以检查Python中的变量。所以,列举一些:
isinstance(obj, type)
函数会使用您的变量obj
并为您提供True
它与您列出的type
的类型相同。
issubclass(obj, class)
函数接收变量obj
,如果True
是obj
的子类,则会为您提供class
。例如,issubclass(Rabbit, Animal)
会为您提供True
值
hasattr
是另一个示例,由此函数演示super_len
:
def super_len(o):
if hasattr(o, '__len__'):
return len(o)
if hasattr(o, 'len'):
return o.len
if hasattr(o, 'fileno'):
try:
fileno = o.fileno()
except io.UnsupportedOperation:
pass
else:
return os.fstat(fileno).st_size
if hasattr(o, 'getvalue'):
# e.g. BytesIO, cStringIO.StringI
return len(o.getvalue())
hasattr
倾向于更多地选择鸭子打字,而且通常更多的是 pythonic ,但这个术语是固执己见的。
正如注释,assert
语句通常用于测试,否则,只需使用if/else
语句。
答案 6 :(得分:2)
通常,你会这样做:
def myFunction(a,b,c):
if not isinstance(a, int):
raise TypeError("Expected int, got %s" % (type(a),))
if b <= 0 or b >= 10:
raise ValueError("Value %d out of range" % (b,))
if not c:
raise ValueError("String was empty")
# Rest of function
答案 7 :(得分:2)
我最近对这个话题做了很多调查,因为我对那里发现的很多libraries不满意。
我最终开发了一个库来解决这个问题,它名为valid8。正如文档中所解释的那样,它主要用于值验证(尽管它也捆绑了简单类型验证函数),您可能希望将其与基于PEP484的类型检查器关联,例如enforce或{{3 }}
这就是你如何单独使用valid8
进行验证(实际上pytypes,以及在你的情况下定义验证逻辑 - 但这不是强制性的):
# for type validation
from numbers import Integral
from valid8 import instance_of
# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len
@validate_arg('a', instance_of(Integral))
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', instance_of(str), Len(s) > 0)
def my_function(a: Integral, b, c: str):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
# check that it works
my_function(0.2, 1, 'r') # InputValidationError for 'a' HasWrongType: Value should be an instance of <class 'numbers.Integral'>. Wrong value: [0.2].
my_function(0, 0, 'r') # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0) # InputValidationError for 'c' Successes: [] / Failures: {"instance_of_<class 'str'>": "HasWrongType: Value should be an instance of <class 'str'>. Wrong value: [0]", 'len(s) > 0': "TypeError: object of type 'int' has no len()"}.
my_function(0, 1, '') # InputValidationError for 'c' Successes: ["instance_of_<class 'str'>"] / Failures: {'len(s) > 0': 'False'}
这是利用PEP484类型提示并将类型检查委托给enforce
的相同示例:
# for type validation
from numbers import Integral
from enforce import runtime_validation, config
config(dict(mode='covariant')) # type validation will accept subclasses too
# for value validation
from valid8 import validate_arg
from mini_lambda import x, s, Len
@runtime_validation
@validate_arg('b', (0 < x) & (x < 10))
@validate_arg('c', Len(s) > 0)
def my_function(a: Integral, b, c: str):
"""an example function I'd like to check the arguments of."""
# check that a is an int
# check that 0 < b < 10
# check that c is not an empty string
# check that it works
my_function(0.2, 1, 'r') # RuntimeTypeError 'a' was not of type <class 'numbers.Integral'>
my_function(0, 0, 'r') # InputValidationError for 'b' [(x > 0) & (x < 10)] returned [False]
my_function(0, 1, 0) # RuntimeTypeError 'c' was not of type <class 'str'>
my_function(0, 1, '') # InputValidationError for 'c' [len(s) > 0] returned [False].
答案 8 :(得分:1)
在调用函数时检查输入参数的类型:
0 1 2
0 1 2 3
1 4 5 6
2 1 2 3
3 4 5 6
4 1 2 3
5 4 5 6
同时检查def func(inp1:int=0,inp2:str="*"):
for item in func.__annotations__.keys():
assert isinstance(locals()[item],func.__annotations__[item])
return (something)
first=7
second="$"
print(func(first,second))
(必须提供断言错误)
答案 9 :(得分:0)
如果要一次性检查**kwargs
,*args
以及普通参数,可以使用locals()
函数作为函数定义中的第一个语句来获取字典论证。
然后使用type()
检查参数,例如在迭代dict时。
def myfunc(my, args, to, this, function, **kwargs):
d = locals()
assert(type(d.get('x')) == str)
for x in d:
if x != 'x':
assert(type(d[x]) == x
for x in ['a','b','c']:
assert(x in d)
whatever more...
答案 10 :(得分:0)
def someFunc(a, b, c):
params = locals()
for _item in params:
print type(params[_item]), _item, params[_item]
演示:
>> someFunc(1, 'asd', 1.0)
>> <type 'int'> a 1
>> <type 'float'> c 1.0
>> <type 'str'> b asd
更多关于locals()
答案 11 :(得分:0)
如果要对多个函数进行验证,可以在装饰器中添加逻辑,如下所示:
def deco(func):
def wrapper(a,b,c):
if not isinstance(a, int)\
or not isinstance(b, int)\
or not isinstance(c, str):
raise TypeError
if not 0 < b < 10:
raise ValueError
if c == '':
raise ValueError
return func(a,b,c)
return wrapper
并使用它:
@deco
def foo(a,b,c):
print 'ok!'
希望这有帮助!
答案 12 :(得分:0)
这不是您的解决方案,但如果您想将函数调用限制为某些特定参数类型,则必须使用PROATOR {Python函数原型验证程序}。你可以参考以下链接。 https://github.com/mohit-thakur-721/proator
答案 13 :(得分:-1)
def myFunction(a,b,c):
"This is an example function I'd like to check arguments of"
if type( a ) == int:
#dostuff
if 0 < b < 10:
#dostuff
if type( C ) == str and c != "":
#dostuff