如何检查对象是否为给定类型,或是否从给定类型继承?

如何检查对象o是否为str类型?


初学者通常错误地期望字符串已经是“数字”——要么期望Python 3.x输入转换类型,要么期望像“1”这样的字符串同时也是整数。对于这些问题,这是错误的规范。请仔细阅读问题,然后使用How do I check if a string representative a number(float or int)?,如何将输入读取为数字?和/或询问用户输入,直到他们给出适当的有效响应。


当前回答

对于更复杂的类型验证,我喜欢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) 

其他回答

检查对象类型的最Python方法是……不检查它。

既然Python鼓励Duck打字,那么您应该尝试。。。除了按照您想要的方式使用对象的方法。因此,如果您的函数正在寻找一个可写的文件对象,不要检查它是否是文件的子类,只需尝试使用它的.write()方法!

当然,有时这些好的抽象会崩溃,而isinstance(obj,cls)正是您所需要的。但要谨慎使用。

我认为使用Python这样的动态语言很酷的一点是你真的不需要检查这样的东西。

我只需要在对象上调用所需的方法并捕获AttributeError。稍后,这将允许您使用其他(看似无关的)对象调用方法,以完成不同的任务,例如模拟对象进行测试。

在使用urllib2.urlopen()从web获取数据时,我经常使用这种方法,它返回一个类似文件的对象。这又可以传递给几乎任何从文件读取的方法,因为它实现了与实际文件相同的read()方法。

但我确信使用isinstance()是有时间和地点的,否则它可能不会出现:)

使用isinstance检查o是str的实例还是str的任何子类:

if isinstance(o, str):

要检查o的类型是否正好是str,不包括str的子类:

if type(o) is str:

有关相关信息,请参见Python库参考中的内置函数。


检查Python 2中的字符串

对于Python 2,这是检查o是否为字符串的更好方法:

if isinstance(o, basestring):

因为这也将捕获Unicode字符串。unicode不是str的子类;str和unicode都是basestring的子类。在Python3中,basestring不再存在,因为字符串(str)和二进制数据(字节)有严格的分隔。

或者,isinstance接受一个类元组。如果o是(str,unicode)的任何子类的实例,则返回True:

if isinstance(o, (str, unicode)):

接受的答案回答问题,因为它提供了所问问题的答案。

Q: 检查给定对象是否为给定类型的最佳方法是什么?检查对象是否继承自给定类型如何?

A: 使用isinstance、issubclass、type基于类型进行检查。

然而,正如其他答案和评论很快指出的那样,“类型检查”的概念比python中的要多得多。自从添加了Python3和类型提示之后,也发生了很多变化。下面,我将介绍类型检查、鸭子键入和异常处理的一些困难。对于那些认为不需要类型检查的人(通常不需要,但我们在这里),我还指出了如何使用类型提示。

类型检查

在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_的类型都是可迭代的。

异常处理和鸭子打字

另一种方法是完全放弃类型检查,转而专注于异常处理和duck类型。也就是说,将代码包装在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,我们都会知道。我们不必在循环输入的任何地方进行检查。我们不必在迭代时存储输入。

此外,这种方法支持duck类型。我们不再检查特定的类型,而是检查特定的行为,并查找输入何时无法按预期运行(在本例中,循环通过nums并能够添加n)。

然而,使异常处理变得好的确切原因也可能是它们的失败。

浮点数不是整数,但它满足工作的行为要求。用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

下面是使用类型提示的一些优点。

代码现在看起来不错!如果您使用类型提示,则可以由编辑器执行静态类型分析!它们存储在函数/类中,使它们可以动态使用,例如类型保护和数据类。当使用help(…)时,它们会显示函数。无需根据描述或更糟糕的缺乏来检查输入类型是否正确。您可以根据结构“键入”提示,例如“它有这个属性吗?”而不需要用户进行子类化。

打字暗示的缺点是什么?

类型提示本身就是语法和特殊文本。这与类型检查不同。

换句话说,它实际上没有回答这个问题,因为它不提供类型检查。然而,无论如何,如果您在这里进行类型检查,那么您也应该进行类型提示。当然,如果您已经得出结论,类型检查实际上是不必要的,但您需要某种类型的外观,那么类型提示适合您。

如果o是str或是从str继承的类型,isinstance(o,str)将返回True。

如果且仅当o是str时,type(o)is str将返回True。如果o是从str继承的类型,则返回False。