40

我有一个 Python 函数,它接受一个数字参数,该参数必须是一个整数才能正确运行。在 Python 中验证这一点的首选方法是什么?

我的第一反应是做这样的事情:

def isInteger(n):
    return int(n) == n

但我不禁认为这是 1) 昂贵 2) 丑陋和 3) 受制于机器 epsilon 的温柔怜悯。

Python 是否提供任何类型检查变量的本机方法?或者这是否被认为违反了语言的动态类型设计?

编辑:因为很多人都问过 - 有问题的应用程序使用 IPv4 前缀,从平面文本文件中获取数据。如果任何输入被解析为浮点数,则该记录应被视为格式错误并被忽略。

4

9 回答 9

49
isinstance(n, int)

如果您需要知道它是否绝对是实际的 int 而不是 int 的子类(通常您不需要这样做):

type(n) is int

这:

return int(n) == n

这不是一个好主意,因为交叉类型比较可能是真的 - 特别是int(3.0)==3.0

于 2009-01-21T00:06:11.430 回答
15

是的,正如埃文所说,不要输入检查。只需尝试使用该值:

def myintfunction(value):
   """ Please pass an integer """
   return 2 + value

那没有类型检查。好多了!让我们看看当我尝试它时会发生什么:

>>> myintfunction(5)
7

这行得通,因为它是一个整数。嗯。让我们尝试一些文本。

>>> 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,无论如何它都应该这样做。如果来电者想抓住它,这是可能的。

如果你进行类型检查,你会怎么做?显示错误对吗?因此,您不必进行类型检查,因为错误已经自动显示。

另外,由于您没有进行类型检查,因此您的函数可以与其他类型一起使用:

浮动:

>>> print myintfunction(2.2)
4.2

复数:

>>> print myintfunction(5j)
(2+5j)

小数点:

>>> import decimal
>>> myintfunction(decimal.Decimal('15'))
Decimal("17")

甚至可以添加数字的完全任意对象!

>>> class MyAdderClass(object):
...     def __radd__(self, value):
...             print 'got some value: ', value
...             return 25
... 
>>> m = MyAdderClass()
>>> print myintfunction(m)
got some value:  2
25

所以你显然通过类型检查一无所获。并且损失很多。


更新:

由于您已经编辑了问题,现在很明显您的应用程序调用了一些仅对 int 有意义的上游例程。

既然如此,我仍然认为您应该将收到的参数传递给上游函数。上游函数将正确处理它,例如,如果需要,会引发错误。我非常怀疑,如果你传递一个浮点数,你处理 IP 的函数会表现得很奇怪。如果您可以给我们图书馆的名称,我们可以为您检查。

但是......如果上游函数会表现不正确并在你传递一个浮点数时杀死一些孩子(我仍然非常怀疑它),那么只需调用int()它:

def myintfunction(value):
   """ Please pass an integer """
   return upstreamfunction(int(value))

您仍然没有进行类型检查,因此您获得了不进行类型检查的最大好处。


如果即使在这之后,您真的想进行类型检查,尽管它会降低您的应用程序的可读性和性能而绝对没有任何好处,请使用 anassert来执行此操作。

assert isinstance(...)
assert type() is xxxx

这样我们就可以关闭s 并通过调用它来从程序assert中删除这个<sarcasm>特性</sarcasm>

python -OO program.py
于 2009-01-21T02:50:17.217 回答
5

Python 现在支持通过typing 模块mypy逐步键入。该模块是 Python 3.5 的 stdlib 的一部分,如果您需要 Python 2 或以前版本的 Python 3 的反向移植,可以从 PyPi下载该模块。您可以通过从命令行运行来安装。typingmypypip install mypy

简而言之,如果您想验证某个函数是否接收 int、float 并返回一个字符串,您可以像这样注释您的函数:

def foo(param1: int, param2: float) -> str:
    return "testing {0} {1}".format(param1, param2)

如果您的文件名为test.py,则可以在安装 mypy 后通过mypy test.py从命令行运行进行类型检查。

如果您使用的是不支持函数注释的旧版 Python,则可以使用类型注释来实现相同的效果:

def foo(param1, param2):
    # type: (int, float) -> str
    return "testing {0} {1}".format(param1, param2)

mypy test.py您对 Python 3 文件和mypy --py2 test.pyPython 2 文件使用相同的命令。

Python 解释器在运行时会完全忽略类型注释,因此它们会产生最小甚至没有开销——通常的工作流程是处理您的代码并定期运行 mypy 以捕获错误和错误。某些 IDE(例如 PyCharm)会理解类型提示,并可以在您直接编辑时提醒您代码中的问题和类型不匹配。

如果出于某种原因,您需要在运行时检查类型(也许您需要验证大量输入?),您应该遵循其他答案中列出的建议——例如 use isinstanceissubclass等。还有一些库,例如强制在运行时尝试执行类型检查(尊重您的类型注释),尽管我不确定它们在撰写本文时的生产准备情况。

有关更多信息和详细信息,请参阅mypy 网站mypy 常见问题解答PEP 484

于 2016-06-29T07:00:10.663 回答
4
if type(n) is int

这会检查是否n是 Python int,并且只是一个 int。它不接受int.

然而,类型检查不适合“Python 方式”。您最好将n其用作 int,如果它引发异常,则捕获它并对其采取行动。

于 2009-01-21T00:08:19.383 回答
1

不要键入检查。鸭子打字的全部意义在于你不应该这样做。例如,如果有人做了这样的事情怎么办:

class MyInt(int):
    # ... extra stuff ...
于 2009-01-21T01:32:11.350 回答
1

使用 Python 进行编程并像使用其他语言一样执行类型检查确实就像选择螺丝刀来敲钉子一样。使用 Python 的异常处理功能更加优雅。

从交互式命令行中,您可以运行如下语句:

int('sometext')

这将产生一个错误 - ipython 告诉我:

<type 'exceptions.ValueError'>: invalid literal for int() with base 10: 'sometext'

现在您可以编写一些代码,例如:

try:
   int(myvar) + 50
except ValueError:
   print "Not a number"

可以对其进行定制以执行所需的任何操作并捕获预期的任何错误。它看起来有点复杂,但符合 Python 的语法和习惯用法,并且代码可读性很强(一旦你习惯了说 Python)。

于 2009-03-25T16:34:53.117 回答
0

我很想这样做:

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)

这样,当你使用它时,任何东西都可以传入,但你只存储一个有效的整数。

于 2009-11-24T11:14:35.247 回答
0

怎么样:

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) 函数......

于 2011-11-24T11:53:15.307 回答
-1

对于那些希望使用 assert() 函数执行此操作的人。以下是如何在代码中有效地进行变量类型检查,而无需定义任何额外的函数。如果出现 assert() 错误,这将阻止您的代码运行。

assert(type(X) == int(0))

如果没有引发错误,代码将继续工作。除此之外,unittest模块对于这类事情来说是一个非常有用的工具。

于 2020-05-24T13:48:02.243 回答