How can I type-check variables in Python?
我有一个python函数,它接受一个数字参数,该参数必须是一个整数,以便它正确地工作。在Python中验证这一点的首选方法是什么?
我的第一反应是这样做:
1 2 | def isInteger(n): return int(n) == n |
但我忍不住想,这是1)昂贵2)丑陋和3)受制于机爱普西隆温柔仁慈。
python是否提供了任何类型检查变量的本地方法?或者这被认为是违反了语言的动态类型设计?
编辑:因为有很多人问过这个问题,所以这个应用程序使用了IPv4前缀,从纯文本文件中获取数据。如果将任何输入解析为浮点,则应将该记录视为格式错误并忽略。
1 | isinstance(n, int) |
如果您需要知道它是否绝对是一个实际的int,而不是int的子类(通常不需要这样做):
1 | type(n) is int |
这是:
1 | return int(n) == n |
这不是一个好主意,因为交叉类型比较是正确的,尤其是
是的,就像埃文说的,不要打字检查。只需尝试使用该值:
1 2 3 | def myintfunction(value): """ Please pass an integer""" return 2 + value |
没有排版检查。好多了!让我们看看当我尝试它时会发生什么:
1 2 | >>> myintfunction(5) 7 |
这是有效的,因为它是一个整数。嗯。让我们试试文字。
1 2 3 4 5 | >>> myintfunction('text') Traceback (most recent call last): File"<stdin>", line 1, in <module> File"<stdin>", line 3, in myintfunction TypeError: unsupported operand type(s) for +: 'int' and 'str' |
它显示一个错误,typeerror,这是它应该做的。如果打电话的人想接电话,那是有可能的。
如果你做了排版检查,你会怎么做?显示错误,对吗?所以您不必进行类型检查,因为错误已经自动出现了。
另外,由于您没有进行类型检查,因此您的函数可以与其他类型一起使用:
Floats:
1 2 | >>> print myintfunction(2.2) 4.2 |
复数:
1 2 | >>> print myintfunction(5j) (2+5j) |
小数:
1 2 3 | >>> import decimal >>> myintfunction(decimal.Decimal('15')) Decimal("17") |
甚至可以添加数字的完全任意对象!
1 2 3 4 5 6 7 8 9 | >>> class MyAdderClass(object): ... def __radd__(self, value): ... print 'got some value: ', value ... return 25 ... >>> m = MyAdderClass() >>> print myintfunction(m) got some value: 2 25 |
所以通过打字你显然什么也得不到。失去很多。
更新:由于您已经编辑了这个问题,现在很明显您的应用程序调用了一些上游例程,这些例程只对ints有意义。
在这种情况下,我仍然认为应该将接收到的参数传递给上游函数。上游函数将正确地处理它,例如,如果需要,会引发错误。我非常怀疑,如果你将ips传递给它一个float,你处理ips的函数会表现得很奇怪。如果你能告诉我们图书馆的名字,我们可以帮你查一下。
但是…如果上游函数的行为不正确,如果你给它传递一个float(我仍然非常怀疑),就会杀死一些孩子,那么只需在它上面调用
1 2 3 | def myintfunction(value): """ Please pass an integer""" return upstreamfunction(int(value)) |
你仍然没有排字,所以你能从没有排字中得到最大的好处。
即便如此,如果您确实希望进行类型检查,尽管这样做会降低应用程序的可读性和性能,但绝对没有任何好处,请使用
1 2 | assert isinstance(...) assert type() is xxxx |
这样我们就可以关闭
1 | python -OO program.py |
python现在支持通过输入模块和mypy逐步输入。从python3.5开始,
简而言之,如果您想验证某个函数接受了int、float并返回了一个字符串,您可以这样注释您的函数:
1 2 | def foo(param1: int, param2: float) -> str: return"testing {0} {1}".format(param1, param2) |
如果您的文件名为
如果使用的是不支持函数注释的旧版本的Python,则可以使用类型注释来实现相同的效果:
1 2 3 | def foo(param1, param2): # type: (int, float) -> str return"testing {0} {1}".format(param1, param2) |
对于python3文件使用相同的命令
类型注释在运行时被python解释器完全忽略,因此它们施加的开销最小到没有——通常的工作流程是处理代码并定期运行mypy来捕获错误和错误。一些IDE(如pycharm)可以理解类型提示,并可以在直接编辑代码时提醒您代码中的问题和类型不匹配。
如果出于某种原因,您需要在运行时检查类型(也许您需要验证大量输入?)您应该遵循其他答案中列出的建议,例如使用
有关更多信息和详细信息,请参阅mypy网站、mypy常见问题解答和PEP 484。
1 | if type(n) is int |
它检查
但是,类型检查不适合"python方式"。最好使用
用python编程并像在其他语言中那样执行类型检查,这就像选择一个螺丝刀来敲入钉子。使用Python的异常处理功能更为优雅。
通过交互式命令行,可以运行如下语句:
1 | int('sometext') |
这将产生一个错误-IPython告诉我:
1 | <type 'exceptions.ValueError'>: invalid literal for int() with base 10: 'sometext' |
现在您可以编写如下代码:
1 2 3 4 | try: int(myvar) + 50 except ValueError: print"Not a number" |
它可以定制以执行所需的任何操作,并捕获预期的任何错误。它看起来有点复杂,但符合Python的语法和习惯用法,并产生非常可读的代码(一旦习惯了讲Python)。
不要输入检查。鸭子打字的关键是你不应该这么做。例如,如果有人做了这样的事情:
1 2 | class MyInt(int): # ... extra stuff ... |
怎么样:
1 2 3 4 5 6 7 8 | def ip(string): subs = string.split('.') if len(subs) != 4: raise ValueError("incorrect input") out = tuple(int(v) for v in subs if 0 <= int(v) <= 255) if len(out) != 4: raise ValueError("incorrect input") return out |
当然有标准的isInstance(3,int)函数…
我很想去做如下的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def check_and_convert(x): x = int(x) assert 0 <= x <= 255,"must be between 0 and 255 (inclusive)" return x class IPv4(object): """IPv4 CIDR prefixes is A.B.C.D/E where A-D are integers in the range 0-255, and E is an int in the range 0-32.""" def __init__(self, a, b, c, d, e=0): self.a = check_and_convert(a) self.b = check_and_convert(a) self.c = check_and_convert(a) self.d = check_and_convert(a) assert 0 <= x <= 32,"must be between 0 and 32 (inclusive)" self.e = int(e) |
这样,当您使用它时,任何东西都可以传入,但您只存储一个有效的整数。