在 Python 中检查类型的规范方法是什么?
- 2024-11-20 08:44:00
- admin 原创
- 6
问题描述:
如何检查对象是否属于给定类型,或者是否从给定类型继承?
如何检查对象是否o
属于该类型str
?
初学者经常错误地认为字符串已经是“数字”——要么期望 Python 3.xinput
转换类型,要么期望像这样的字符串同时也是'1'
整数。这对于这些问题来说是错误的规范。请仔细阅读问题,然后使用如何检查字符串是否代表数字(浮点数或整数)?、如何将输入读取为数字?和/或要求用户输入,直到他们给出有效的响应。
解决方案 1:
用于isinstance
检查是否o
是的实例str
或任何子类str
:
if isinstance(o, str):
要检查 的类型是否o
恰好str
,不包括 的子类str
:
if type(o) is str:
相关信息请参阅Python 库参考中的内置函数。
检查 Python 2 中的字符串
o
对于 Python 2,这是检查是否为字符串的更好方法:
if isinstance(o, basestring):
因为这也将捕获 Unicode 字符串。unicode
不是 的子类str
;str
和unicode
都是 的子类basestring
。在 Python 3 中,basestring
不再存在,因为字符串 ( ) 和二进制数据 ( ) 有严格的区分。str
`bytes`
或者,接受类的元组。如果是以下任一子类的实例,isinstance
则返回:True
`o`(str, unicode)
if isinstance(o, (str, unicode)):
解决方案 2:
检查对象类型的最 Python 方式是......不检查它。
由于 Python 鼓励使用Duck Typing,因此您应该按照try...except
自己想要的方式使用对象的方法。因此,如果您的函数正在寻找可写文件对象,请不要检查它是否是 的子类file
,只需尝试使用它的.write()
方法!
当然,有时这些漂亮的抽象会失效,而isinstance(obj, cls)
这正是你所需要的。但要谨慎使用。
解决方案 3:
isinstance(o, str)
`True如果
o是
str或 是从 继承的类型,则返回
str`。
type(o) is str
`True当且仅当
o是时才会返回。如果是继承自 的类型,
str它将返回。
Falseo
str`
解决方案 4:
在Python 3.10中,你可以|
使用isinstance
:
>>> isinstance(1223, int | str)
True
>>> isinstance('abcd', int | str)
True
解决方案 5:
在提出并回答了这个问题之后,类型提示被添加到了 Python 中。Python 中的类型提示允许检查类型,但方式与静态类型语言截然不同。Python 中的类型提示将预期的参数类型与函数相关联,作为与函数关联的运行时可访问数据,这允许检查类型。类型提示语法示例:
def foo(i: int):
return i
foo(5)
foo('oops')
在这种情况下,我们希望触发错误,foo('oops')
因为参数的注释类型是int
。添加的类型提示不会导致脚本正常运行时发生错误。但是,它会向函数添加描述预期类型的属性,其他程序可以查询并使用这些属性来检查类型错误。
可以用来查找类型错误的其他程序之一是mypy
:
mypy script.py
script.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "int"
(您可能需要mypy
从包管理器安装。我认为它不是随 CPython 一起提供的,但似乎具有一定程度的“官方性”。)
这种方式的类型检查与静态类型编译语言中的类型检查不同。由于 Python 中的类型是动态的,因此必须在运行时进行类型检查,如果我们坚持每次都进行类型检查,那么即使对正确的程序也会造成成本。显式类型检查也可能比需要的限制更严格,并导致不必要的错误(例如,参数是否真的需要完全是list
类型,或者任何可迭代的就足够了?)。
显式类型检查的优点是它可以更早地发现错误,并提供比鸭子类型更清晰的错误消息。鸭子类型的确切要求只能通过外部文档来表达(希望它是全面和准确的),并且不兼容类型的错误可能发生在远离其起源的地方。
Python 的类型提示旨在提供一种折衷方案,可以指定和检查类型,但在通常的代码执行过程中不会产生额外的成本。
该typing
软件包提供了类型变量,可用于类型提示以表达所需行为,而无需特定类型。例如,它包含诸如Iterable
和Callable
等变量,用于提示以指定对具有这些行为的任何类型的需求。
虽然类型提示是检查类型最 Pythonic 的方式,但完全不检查类型而依靠鸭子类型往往更 Pythonic。类型提示相对较新,目前尚不清楚它们何时才是最 Pythonic 的解决方案。一个相对没有争议但非常普遍的比较:类型提示提供了一种可以强制执行的文档形式,允许代码更早且更容易理解地生成错误,可以捕获鸭子类型无法捕获的错误,并且可以静态检查(以一种不寻常的方式,但它仍然在运行时之外)。另一方面,鸭子类型长期以来一直是 Pythonic 的方式,不会带来静态类型的认知开销,更简洁,并且会接受所有可行的类型,甚至更多。
解决方案 6:
isinstance(o, str)
链接至文档
解决方案 7:
您可以使用类型的 name 检查变量的类型。
前任:
>>> a = [1,2,3,4]
>>> b = 1
>>> type(a).__name__
'list'
>>> type(a).__name__ == 'list'
True
>>> type(b).__name__ == 'list'
False
>>> type(b).__name__
'int'
解决方案 8:
对于更复杂的类型验证,我喜欢typeguard基于 python 类型提示注释进行验证的方法:
from typeguard import check_type
from typing import List
try:
check_type('mylist', [1, 2], List[int])
except TypeError as e:
print(e)
您可以以非常干净且易读的方式执行非常复杂的验证。
check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo)
解决方案 9:
被接受的答案回答了问题,因为它提供了所提问题的答案。
问:检查给定对象是否属于给定类型的最佳方法是什么?如何检查对象是否从给定类型继承?
答:
isinstance, issubclass, type
根据类型来检查。
然而,正如其他答案和评论很快指出的那样,“类型检查”的概念远不止 Python 中的类型检查。自从添加了 Python 3 和类型提示以来,很多事情也发生了变化。下面,我将介绍类型检查、鸭子类型和异常处理的一些困难。对于那些认为类型检查不是必需的(通常不需要,但我们在这里),我还指出了如何使用类型提示来代替。
类型检查
在 Python 中,类型检查并不总是一件合适的事情。请考虑以下示例:
def sum(nums):
"""Expect an iterable of integers and return the sum."""
result = 0
for n in nums:
result += n
return result
要检查输入是否是整数的可迭代对象,我们遇到了一个大问题。检查每个元素是否都是整数的唯一方法是循环检查每个元素。但是如果我们循环整个迭代器,那么预期的代码将一无所有。在这种情况下,我们有两种选择。
循环时进行检查。
提前检查,但在我们检查后存储所有内容。
选项 1 的缺点是会使我们的代码复杂化,特别是如果我们需要在很多地方执行类似的检查。它迫使我们将类型检查从函数顶部移到代码中使用迭代器的所有地方。
选项 2 的缺点很明显,它破坏了迭代器的整个用途。迭代器的整个用途就是不存储数据,因为我们不需要这样做。
有人可能认为检查所有元素太多了,那么也许我们可以只检查输入本身是否是可迭代类型,但实际上没有任何可迭代基类。任何实现的类型__iter__
都是可迭代的。
异常处理和鸭子类型
另一种方法是完全放弃类型检查,转而专注于异常处理和鸭子类型。也就是说,将代码包装在 try-except 块中并捕获发生的任何错误。或者,什么也不做,让异常自然地从代码中出现。
这是捕获异常的一种方法。
def sum(nums):
"""Try to catch exceptions?"""
try:
result = 0
for n in nums:
result += n
return result
except TypeError as e:
print(e)
与之前的选项相比,这当然更好。我们在运行代码时进行检查。如果有任何TypeError
地方,我们都会知道。我们不必在循环输入的每个地方都进行检查。我们也不必在迭代输入时存储输入。
此外,这种方法还支持鸭子类型。我们不再检查specific types
,而是检查specific behaviors
,并在输入未能按预期运行时查找 (在本例中,循环nums
并能够添加n
)。
然而,异常处理之所以优秀的原因也可能是其失败的原因。
A
float
不是int
,但它满足工作的行为要求。用 try-except 块包装整个代码也是不好的做法。
起初这些可能看起来不像是问题,但下面的一些原因可能会改变你的想法。
用户不再能期望我们的函数
int
按预期返回。这可能会破坏其他地方的代码。由于异常可能来自各种各样的来源,因此在整个代码块上使用 try-except 可能会捕获您不想要的异常。我们只想检查是否
nums
可迭代且具有整数元素。理想情况下,我们希望捕获代码生成器中的异常,并在其位置引发更具信息量的异常。当其他人的代码引发异常而没有任何解释,除了一行你没有写的代码和一些
TypeError
发生的异常时,这可不是什么好事。
为了修复上述问题的异常处理,我们的代码就会变成这样……令人憎恶。
def sum(nums):
"""
Try to catch all of our exceptions only.
Re-raise them with more specific details.
"""
result = 0
try:
iter(nums)
except TypeError as e:
raise TypeError("nums must be iterable")
for n in nums:
try:
result += int(n)
except TypeError as e:
raise TypeError("stopped mid iteration since a non-integer was found")
return result
你可以大概知道这是怎么回事。我们越是试图“正确”地检查,我们的代码看起来就越糟糕。与原始代码相比,这根本无法阅读。
我们可以争辩说这也许有点极端。但另一方面,这只是一个非常简单的例子。实际上,你的代码可能比这复杂得多。
类型提示
我们已经看到,当我们尝试修改我们的小示例以“启用类型检查”时会发生什么。类型提示不是专注于尝试强制使用特定类型,而是提供了一种让用户清楚了解类型的方法。
from typing import Iterable
def sum(nums: Iterable[int]) -> int:
result = 0
for n in nums:
result += n
return result
使用类型提示有一些优点。
代码现在看起来确实不错!
如果您使用类型提示,您的编辑器可能会执行静态类型分析!
它们存储在函数/类中,使它们可动态使用,例如
typeguard
和dataclasses
。它们在使用时显示函数
help(...)
。无需根据描述或更糟糕的缺乏描述来检查您的输入类型是否正确。
您可以根据结构“键入”提示,例如“它有这个属性吗?”而无需用户进行子类化。
类型提示的缺点是什么?
类型提示本身不过是语法和特殊文本而已。它与类型检查不同。
换句话说,它实际上并没有回答这个问题,因为它没有提供类型检查。无论如何,如果你来这里是为了进行类型检查,那么你也应该使用类型提示。当然,如果你得出结论,类型检查实际上不是必要的,但你想要一些类似的输入,那么类型提示就适合你。
解决方案 10:
我认为使用像 Python 这样的动态语言的妙处在于你真的不必检查这样的事情。
我只需在您的对象上调用所需的方法并捕获AttributeError
。稍后,这将允许您使用其他(看似不相关的)对象调用您的方法来完成不同的任务,例如模拟对象进行测试。
我在从网上获取数据时经常使用这个方法,urllib2.urlopen()
它会返回一个类似文件的对象。这反过来可以传递给几乎任何从文件读取的方法,因为它实现的read()
方法与真实文件相同。
但我确信有使用它的时间和地点isinstance()
,否则它可能不会在那里:)
解决方案 11:
致 Hugo:
您可能指的是list
而不是array
,但这指出了类型检查的整个问题 - 您不想知道所讨论的对象是否是列表,您想知道它是某种序列还是单个对象。因此,请尝试像序列一样使用它。
假设你想将对象添加到现有序列中,或者如果它是对象序列,则将它们全部添加
try:
my_sequence.extend(o)
except TypeError:
my_sequence.append(o)
一个技巧是,如果你正在处理字符串和/或字符串序列 - 这很棘手,因为字符串通常被认为是单个对象,但它也是一个字符序列。更糟糕的是,因为它实际上是一个单一长度的字符串序列。
[ ]
我通常会选择将 API 设计为只接受单个值或序列 - 这样可以让事情变得简单。如果需要,在传递单个值时将其放在一个位置并不难。
(尽管这可能会导致字符串出现错误,因为它们看起来像(是)序列。)
解决方案 12:
检查浮点类型
isinstance(x, float)
或者
type(x) is float
如果类型不完全是浮点数,则会返回False
,例如 numpy.float32:
import pandas as pd
import numpy as np
x = pd.Series([0.2], dtype=np.float32)[0]
正如该帖子的评论所指出的,numpyissubdtype
可用于此目的。
检测浮点类型:
np.issubdtype(type(x), np.floating)
2.0
对于、pd.Series([2.0], dtype=np.float32)[0]
以及通常任何浮点数、np.float32、np.float64 ... 类型,返回 True2
对于、"2.0"
(字符串)、[2.0, 1.2]
(列表)等,返回 False
检测数字(浮点型和整数型)类型:
np.issubdtype(type(x), np.number)
2
对于、2.0
、pd.Series([2.0], dtype=np.float32)[0]
、pd.Series([2.0], dtype=np.int32)[0]
以及通常任何 float、np.float32、np.float64 ... 类型,返回 True对于
"2"
(string)、[2.0, 1.2]
(list) 等,返回 False
要检测 pd.Index 类类型:
np.issubdtype(type(x), pd.Index)
pd.Index
对于,pd.Int64Index
,pd.Float64Index
, ...返回 True
解决方案 13:
str
如果您必须检查或的类型,int
请使用instanceof
。正如其他人已经提到的,解释是还包括子类。在我看来,子类的一个重要示例是数据类型为IntEnum
或的枚举StrEnum
。这是一种定义相关常量的非常好的方法。但是,如果库不接受这些类型,那就有点烦人了。
例子:
import enum
class MyEnum(str, enum.Enum):
A = "a"
B = "b"
print(f"is string: {isinstance(MyEnum.A, str)}") # True
print(f"is string: {type(MyEnum.A) == str}") # False!!!
print(f"is string: {type(MyEnum.A.value) == str}") # True
解决方案 14:
在 Python 中,您可以使用内置isinstance()
函数来检查对象是否属于给定类型,或者是否从给定类型继承。
要检查对象 o 是否属于 str 类型,可以使用以下代码:
if isinstance(o, str):
# o is of type str
您还可以使用type()
函数来检查对象类型。
if type(o) == str:
# o is of type str
您还可以使用函数检查对象是否是特定类的子类issubclass()
。
if issubclass(type(o),str):
# o is sub class of str
解决方案 15:
要检查对象是否属于给定类型或是否从给定类型继承,可以使用该isinstance()
函数。以下是检查对象 o 是否属于 str 类型的方法:
o = "Hello, world!"
if isinstance(o, str):
print("The object is of type str.")
else:
print("The object is not of type str.")
解决方案 16:
Python 3.10 引入了一种新方法来检查类型,这种方法通常比match 语句if/instanceof()
更简洁。它可以检查多种类型:
float(x) # Is it a floating point?
str(x) # Is it a string?
int(x) # Is it an integer?
...
例如,您可以这样处理一些返回多种类型的函数,每个类型都需要以不同的方式处理:
def read_user_input() -> float | str | None:
...
# Get user input, rounding floats to 2 decimal places.
def user_input() -> str:
match read_user_input()
case float(a_number):
n = str(round(2, a_number))
case str(a_string):
n = return str(round(2, float(a_string.strip())))
case None:
n = "(No input given)"
return n
奖励:当您以这种方式检查类型时,类型检查器可以验证是否match
详尽;您已经处理了函数将给您的所有可能类型。
解决方案 17:
如果您还想要整数,则可以使用:
integer = 1
check_integer = integer.isdigit()
isdigit()
返回 True 或 False,您可以在中使用它while-loop
。
解决方案 18:
检查类型的一种简单方法是将其与您知道类型的东西进行比较。
>>> a = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True
解决方案 19:
我认为最好的方法是正确输入变量。您可以使用“typeing”库来做到这一点。
例子:
from typing import NewType
UserId = NewType ('UserId', int)
some_id = UserId (524313`)
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件