如何在 Python 中对变量进行类型检查?

发布于 2024-07-12 08:35:29 字数 382 浏览 14 评论 0 原文

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

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

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

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

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

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

I have a Python function that takes a numeric argument that must be an integer in order for it behave correctly. What is the preferred way of verifying this in Python?

My first reaction is to do something like this:

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

But I can't help thinking that this is 1) expensive 2) ugly and 3) subject to the tender mercies of machine epsilon.

Does Python provide any native means of type checking variables? Or is this considered to be a violation of the language's dynamically typed design?

EDIT: since a number of people have asked - the application in question works with IPv4 prefixes, sourcing data from flat text files. If any input is parsed into a float, that record should be viewed as malformed and ignored.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

沧笙踏歌 2024-07-19 08:35:29
isinstance(n, int)

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

type(n) is int

this:

return int(n) == n

不是一个好主意,因为跨类型比较可以是正确的 - 特别是 <代码>int(3.0)==3.0

isinstance(n, int)

If you need to know whether it's definitely an actual int and not a subclass of int (generally you shouldn't need to do this):

type(n) is int

this:

return int(n) == n

isn't such a good idea, as cross-type comparisons can be true - notably int(3.0)==3.0

執念 2024-07-19 08:35:29

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

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

所以通过类型检查你显然什么也得不到。 而且损失很多。


更新:

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

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

但是......如果上游函数将表现不正确并杀死一些孩子,如果你向它传递一个浮点数(我仍然高度怀疑它),那么只需调用 int() 就可以了:

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

你仍然不进行类型检查,因此您可以获得不进行类型检查的最大好处。


如果即使在所有这些之后,您确实想要进行类型检查,尽管它会降低应用程序的可读性和性能,但毫无好处,那么请使用 assert 来执行此操作。

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

这样我们就可以关闭 assert 并从以下位置删除此功能程序通过调用它作为

python -OO program.py

Yeah, as Evan said, don't type check. Just try to use the value:

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

That doesn't have a typecheck. It is much better! Let's see what happens when I try it:

>>> myintfunction(5)
7

That works, because it is an integer. Hm. Lets try some text.

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

It shows an error, TypeError, which is what it should do anyway. If caller wants to catch that, it is possible.

What would you do if you did a typecheck? Show an error right? So you don't have to typecheck because the error is already showing up automatically.

Plus since you didn't typecheck, you have your function working with other types:

Floats:

>>> print myintfunction(2.2)
4.2

Complex numbers:

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

Decimals:

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

Even completely arbitrary objects that can add numbers!

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

So you clearly get nothing by typechecking. And lose a lot.


UPDATE:

Since you've edited the question, it is now clear that your application calls some upstream routine that makes sense only with ints.

That being the case, I still think you should pass the parameter as received to the upstream function. The upstream function will deal with it correctly e.g. raising an error if it needs to. I highly doubt that your function that deals with IPs will behave strangely if you pass it a float. If you can give us the name of the library we can check that for you.

But... If the upstream function will behave incorrectly and kill some kids if you pass it a float (I still highly doubt it), then just just call int() on it:

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

You're still not typechecking, so you get most benefits of not typechecking.


If even after all that, you really want to type check, despite it reducing your application's readability and performance for absolutely no benefit, use an assert to do it.

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

That way we can turn off asserts and remove this <sarcasm>feature</sarcasm> from the program by calling it as

python -OO program.py
梦过后 2024-07-19 08:35:29

Python 现在支持通过 逐步输入。 org/3/library/typing.html" rel="nofollow noreferrer">打字模块 和 mypy。 从 Python 3.5 开始,typing 模块是 stdlib 的一部分,可以下载 如果您需要 Python 2 或以前版本的 Python 3 的向后移植,请从 PyPi 获取。您可以通过从命令行运行 pip install mypy 来安装 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)

您对 Python 3 文件使用相同的命令 mypy test.py,并且 < code>mypy --py2 test.py 用于 Python 2 文件。

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

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

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

Python now supports gradual typing via the typing module and mypy. The typing module is a part of the stdlib as of Python 3.5 and can be downloaded from PyPi if you need backports for Python 2 or previous version of Python 3. You can install mypy by running pip install mypy from the command line.

In short, if you want to verify that some function takes in an int, a float, and returns a string, you would annotate your function like so:

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

If your file was named test.py, you could then typecheck once you've installed mypy by running mypy test.py from the command line.

If you're using an older version of Python without support for function annotations, you can use type comments to accomplish the same effect:

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

You use the same command mypy test.py for Python 3 files, and mypy --py2 test.py for Python 2 files.

The type annotations are ignored entirely by the Python interpreter at runtime, so they impose minimal to no overhead -- the usual workflow is to work on your code and run mypy periodically to catch mistakes and errors. Some IDEs, such as PyCharm, will understand type hints and can alert you to problems and type mismatches in your code while you're directly editing.

If, for some reason, you need the types to be checked at runtime (perhaps you need to validate a lot of input?), you should follow the advice listed in the other answers -- e.g. use isinstance, issubclass, and the like. There are also some libraries such as enforce that attempt to perform typechecking (respecting your type annotations) at runtime, though I'm uncertain how production-ready they are as of time of writing.

For more information and details, see the mypy website, the mypy FAQ, and PEP 484.

八巷 2024-07-19 08:35:29
if type(n) is int

这会检查 n 是否为 Python int,并且为 int。 它不接受 int 的子类。

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

if type(n) is int

This checks if n is a Python int, and only an int. It won't accept subclasses of int.

Type-checking, however, does not fit the "Python way". You better use n as an int, and if it throws an exception, catch it and act upon it.

平安喜乐 2024-07-19 08:35:29

不要输入检查。 鸭子打字的重点是你不应该这样做。 例如,如果有人做了这样的事情怎么办:

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

Don't type check. The whole point of duck typing is that you shouldn't have to. For instance, what if someone did something like this:

class MyInt(int):
    # ... extra stuff ...
小女人ら 2024-07-19 08:35:29

使用 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)。

Programming in Python and performing typechecking as you might in other languages does seem like choosing a screwdriver to bang a nail in with. It is more elegant to use Python's exception handling features.

From an interactive command line, you can run a statement like:

int('sometext')

That will generate an error - ipython tells me:

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

Now you can write some code like:

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

That can be customised to perform whatever operations are required AND to catch any errors that are expected. It looks a bit convoluted but fits the syntax and idioms of Python and results in very readable code (once you become used to speaking Python).

女皇必胜 2024-07-19 08:35:29

我很想这样做:

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)

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

I would be tempted to to something like:

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)

That way when you are using it anything can be passed in yet you only store a valid integer.

满天都是小星星 2024-07-19 08:35:29

怎么样:

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

how about:

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

ofcourse there is the standard isinstance(3, int) function ...

难忘№最初的完美 2024-07-19 08:35:29

对于那些希望使用assert()函数来做到这一点的人。 以下是如何在代码中有效地放置变量类型检查而无需定义任何其他函数。 如果出现assert() 错误,这将阻止您的代码运行。

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

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

For those who are looking to do this with assert() function. Here is how you can efficiently place the variable type check in your code without defining any additional functions. This will prevent your code from running if the assert() error is raised.

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

If no error was raised, code continues to work. Other than that, unittest module is a very useful tool for this sorts of things.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文