English 中文(简体)
我怎么在Python中进行变量类型检查?
原标题:
  • 时间:2009-01-20 23:58:45
  •  标签:

我有一个Python函数,它需要一个数字参数,该参数必须是整数,才能正确地工作。在Python中,验证这一点的首选方法是什么?

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

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

但我忍不住想,这是1)昂贵的 2)丑陋的,而且3)受到机器epsilon的温柔怜悯。

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

编辑:因为很多人问了 - 相关应用程序使用 IPv4 前缀,从平面文本文件中获取数据。如果将任何输入解析为浮点数,则应将该记录视为格式不正确并忽略。

最佳回答
isinstance(n, int)

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

type(n) is int

这个:

return int(n) == n

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

问题回答

是的,就像Evan说的,不要进行类型检查。只需尝试使用该值即可。

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 

它显示了一个错误类型错误,这本来就是应该的。如果调用者想要捕获它,是可能的。

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

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

浮点数:

>>> 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

所以很明显,通过类型检查你什么都得不到,但会失去很多。


UPDATE:

由于您编辑了问题,现在清楚的是,您的应用程序调用了一些只使用整数才有意义的上游例程。

既然如此,我仍然认为您应该将参数按接收到的方式传递给上游函数。上游函数将正确地处理它,例如如果需要则引发错误。我非常怀疑如果您将浮点数传递给处理IP的函数,它不会出现奇怪的行为。如果您可以告诉我们库的名称,我们可以为您检查。

但是...如果上游函数的行为不正确,并且在你将其传递给它时会杀死一些孩子(我仍然非常怀疑),那么只需要调用int()即可:

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

你仍然没有进行类型检查,因此你可以获得大部分不进行类型检查而获得的好处。


即使经过这一切,你真的想进行类型检查,尽管它降低了应用程序的可读性和性能,而且没有任何好处,那么可以使用assert进行检查。

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

这样我们可以关闭assert并通过将其称为来删除程序中的<sarcasm>特性</sarcasm>

python -OO program.py

Python现在通过typing模块和mypy支持渐进式类型。typing模块是Python 3.5的一部分,如果需要Python 2或之前版本的后移,可以从PyPi下载。您可以通过从命令行运行“pip install mypy”来安装mypy。

简而言之,如果您想验证某个函数接受 int、float 并返回 string,您可以像这样注释您的函数:

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.py检查Python 2文件。

类型注解在运行时完全被Python解释器忽略,因此它们对运行时没有或者只有极小的开销——通常的工作流程是在编写代码的同时定期运行mypy以便捕捉错误和问题。一些集成开发环境,例如PyCharm,会理解类型提示并在直接编辑代码时提示问题和类型不匹配。

如果由于某种原因,您需要在运行时检查类型(也许您需要验证大量的输入?),那么您应该遵循其他答案中列出的建议 - 例如使用 isinstanceissubclass等。 还有一些库,例如enforce,它试图在运行时执行类型检查(遵守您的类型注释),尽管我不确定它们是否可用于生产环境。

有关更多信息和详细信息,请参见mypy网站mypy FAQPEP 484

if type(n) is int

这个函数检查n是否是Python中的整数,而且只有整数。它不允许int的子类。

然而,类型检查并不符合“Python的方式”。你最好将n用作int,如果它抛出异常,请捕获它并采取相应措施。

不要进行类型检查。鸭子类型的整个意义在于您不应该这样做。例如,如果有人像这样做了什么:

class MyInt(int):
    # ... extra stuff ...

在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)。

我会被诱惑做一些类似的事情:

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)

这样,当你使用它时,任何内容都可以被传递,但你只会存储一个有效的整数。

怎么样? (zěn me yàng?)

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

对于那些想要使用assert()函数来实现此操作的人。以下是您可以在代码中有效地放置变量类型检查的方式,而无需定义任何其他函数。如果引发assert()错误,则会阻止您的代码运行。

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

如果没有出现错误,代码就会继续工作。除此之外,unittest模块是这类事情非常有用的工具。





相关问题
热门标签