Best way to check function arguments in Python
我正在寻找一种有效的方法来检查python函数的变量。 例如,我想检查参数类型和值。 有这个模块吗? 或者我应该使用类似装饰器或任何特定习语的东西?
1 2 3 4 5 | 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 |
在这个细长的答案中,我们实现了一个基于PEP 484样式类型提示的Python 3.x特定类型检查装饰器,其中少于275行纯Python(大多数是解释性文档字符串和注释) - 针对工业进行了大量优化 - 力量真实世界的使用完成与
熊的打字意外真棒的盛宴:
1 2 3 4 5 | >>> @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.,这实际上并不那么令人印象深刻。
Pure Bruteforce Hardcore效率
在我的有限领域知识中,熊类型在空间和时间方面都比使用Python中的所有现有类型检查实现更有效。 (稍后会详细介绍。)
然而,效率通常在Python中无关紧要。如果是这样,你将不会使用Python。类型检查是否实际上偏离了避免在Python中过早优化的既定规范?是。是的,它确实。
考虑分析,这为每个感兴趣的分析度量(例如,函数调用,行)增加了不可避免的开销。为了确保准确的结果,通过利用优化的C扩展(例如,
类型检查也不例外。类型检查会增加应用程序检查的每个函数调用类型的开销 - 理想情况下,所有函数调用类型。为了防止善意(但可悲的是心胸狭隘)的同事在上周五将咖啡因添加的allnighter添加到您的老年传统Django网络应用程序之后删除您默认添加的类型检查,类型检查必须快速。如此之快,以至于当你在不告诉任何人的情况下添加它时,没有人注意到它就在那里。我一直这样做!如果你是同事,请停止阅读。
但是,如果即使是荒谬的速度对于你的glutorous应用程序来说还不够,那么通过启用Python优化(例如,通过将
1 2 3 4 | $ 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中类型检查的手动方法。熊类型旨在不施加性能损失,兼容性约束或第三方依赖性(无论如何都超过了手动方法所强加的)。熊类型可以无缝地集成到现有的代码库和测试套件中而无需修改。
每个人都可能熟悉手动方法。您手动
准备你的呕吐袋。为简洁起见,我们假设一个简化的
1 2 3 4 5 | 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,对吗?我们许多人都通过了这门课。
Bear输入将上述方法手动执行的类型检查提取到动态定义的包装函数中,自动执行相同的检查 - 增加了粒度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
这是啰嗦。但它基本上也和手动方法一样快。 *暗示建议。
请注意包装函数中完全没有函数检查或迭代,其中包含与原始函数类似的测试次数 - 尽管测试是否以及如何将要检查的类型参数传递给的函数的额外(可忽略不计)成本当前函数调用。你无法赢得每场战斗。
实际上是否可以可靠地生成这样的包装函数,以便在少于275行纯Python中键入检查任意函数? Snake Plisskin说:"真实的故事。抽烟了吗?"
是的。我可能有一个领带。
不,Srsly。为什么"忍受"?
熊跳鸭。鸭子可能会飞,但熊可能会把鸭子扔到鸭子身上。在加拿大,大自然会给你带来惊喜。
下一个问题。
无论如何,熊有什么好热的?
现有的解决方案不执行裸机类型检查 - 至少,我没有进行过任何检查。它们都迭代地重新检查每个函数调用上类型检查函数的签名。虽然对于单个呼叫可忽略不计,但在通过所有呼叫进行聚合时,重新检查开销通常是不可忽略的。真的,真的不可忽视。
然而,这不仅仅是效率问题。现有解决方案通常也无法解决常见的边缘情况。这包括大多数(如果不是全部)玩具装饰器在此处和其他地方作为stackoverflow答案提供。经典失败包括:
在无熊失败的地方,熊的打字成功。所有人都承受!
熊打字无人问津
熊类型将检查函数签名的空间和时间成本从函数调用时间转移到函数定义时间 - 即,从
熊打字是尝试让你的类型检查蛋糕并吃它。为此,
我们可以?让我们深入了解深渊。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | # 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说,让
警告,诅咒和空洞的承诺
没有什么是完美的。即使是熊打字。
警告I:未选中默认值
Bear输入不会键入检查未分配参数分配的默认值。从理论上讲,它可以。但不是275行或更少,当然不是作为stackoverflow的答案。
安全(可能完全不安全)的假设是功能实现者声称他们在定义默认值时知道他们在做什么。由于默认值通常是常量(...它们最好是!),重新检查在每个函数调用中永不改变的常量类型,分配一个或多个默认值将违反熊类型的基本原则:"不要重复你自己过来了,oooover再次oooo-oooover。"
告诉我错了,我会用upvotes淋浴你。
警告II:没有PEP 484
PEP 484("类型提示")形式化了PEP 3107首先引入的函数注释的使用("函数注释")。 Python 3.5通过一个新的顶级
熊打字不支持它们。从理论上讲,它可以。但不是275行或更少,当然不是作为stackoverflow的答案。
然而,熊类型确实支持类型的联合,就像
测试或它没有发生
这是它的要点。得到它,要点?我现在就停下来。
与
现在强制性的脖子咆哮没有人要求。
API暴力史
Python 3.5没有为使用PEP 484类型提供实际支持。笏?
这是真的:没有类型检查,没有类型推断,没有类型nuthin'。相反,开发人员应该通过重量级的第三方CPython解释器包装器来定期运行他们的整个代码库,实现这种支持的传真(例如,mypy)。当然,这些包装纸强加:
我问Guido:"为什么?如果你不愿意用一个具体的API来实现那个抽象的东西,为什么还要发明一个抽象的API呢?"为什么要把一百万Pythonistas的命运留给自由开源市场的关节炎手?为什么要在官方Python stdlib中创建另一个可以通过275行装饰器轻松解决的技术问题?
我没有Python,我必须尖叫。
好。
最Pythonic的习惯用法是清楚地记录函数所期望的内容,然后尝试使用传递给函数的任何内容,并让异常传播或者只是捕获属性错误并引发
验证真正有意义的唯一地方是系统或子系统入口点,例如Web表单,命令行参数等。在其他地方,只要您的函数被正确记录,调用者就有责任传递适当的参数。
编辑:自2019年起,在Python中使用类型注释和静态检查的支持更多;看看打字模块和mypy。 2013年答案如下:
类型检查通常不是Pythonic。在Python中,更常见的是使用duck typing。例:
在您的代码中,假设参数(在您的示例中为
1 2 | def my_function(a): return a + 7 |
这意味着您的函数不仅可以使用整数,还可以使用浮点数和任何定义了
1 2 3 4 | def my_function(a): b = int(a) + 7 c = (5, 6, 3, 123541)[b] return c |
并且该函数仍适用于定义
在回答你的其他问题时,我认为这是最好的(正如其他答案所说的那样:
1 2 3 | def my_function(a, b, c): assert 0 < b < 10 assert c # A non-empty string has the Boolean value True |
要么
1 2 3 4 5 6 7 8 9 | 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 |
我做的一些类型检查装饰器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | 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)) |
一种方法是使用
1 2 3 4 5 6 | 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' |
您可以使用Type Enforcement接受/返回装饰器
PythonDecoratorLibrary
它非常简单易读:
1 2 3 | @accepts(int, int, float) def myfunc(i1, i2, i3): pass |
有多种方法可以检查Python中的变量。所以,列举一些:
-
isinstance(obj, type) 函数接受您的变量obj 并为您提供True 它与您列出的type 的类型相同。 -
issubclass(obj, class) 接受变量obj 的函数,如果obj 是class 的子类,则为True 。所以例如issubclass(Rabbit, Animal) 会给你一个True 值 -
hasattr 是另一个示例,由此函数演示,super_len :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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()) |
正如注释,
我最近对这个话题做了很多调查,因为我对那里发现的很多图书馆都不满意。
我最终开发了一个库来解决这个问题,它被命名为valid8。 正如文档中所解释的那样,它主要用于值验证(尽管它也捆绑了简单的类型验证函数),您可能希望将它与基于PEP484的类型检查器(例如enforce或pytypes)相关联。
这是您在单独使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # 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类型提示并将类型检查委托给
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 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]. |
通常,你做这样的事情:
1 2 3 4 5 6 7 8 9 | 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 |
这会在调用函数时检查输入参数的类型:
1 2 3 4 5 6 7 8 9 10 | 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)) |
还要检查
这不是您的解决方案,但如果您想将函数调用限制为某些特定的参数类型,则必须使用PROATOR {Python函数原型验证器}。你可以参考以下链接。 https://github.com/mohit-thakur-721/proator
如果要对多个函数进行验证,可以在装饰器中添加逻辑,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 | 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 |
并使用它:
1 2 3 | @deco def foo(a,b,c): print 'ok!' |
希望这可以帮助!
1 2 3 4 | def someFunc(a, b, c): params = locals() for _item in params: print type(params[_item]), _item, params[_item] |
演示:
1 2 3 4 | >> someFunc(1, 'asd', 1.0) >> <type 'int'> a 1 >> <type 'float'> c 1.0 >> <type 'str'> b asd |
更多关于当地人()
如果要一次性检查
然后使用
1 2 3 4 5 6 7 8 9 10 | 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... |
1 2 3 4 5 6 7 8 | 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 |