在Python中检查类型的规范方法是什么?

时间:2008-09-30 11:00:10

标签: python types

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

假设我有一个对象o。如何检查它是否为str

15 个答案:

答案 0 :(得分:1313)

要检查ostr的实例还是str的任何子类,请使用isinstance(这将是“规范”方式):

if isinstance(o, str):

检查o的类型是否正好str(排除子类):

if type(o) is str:

以下内容也有效,在某些情况下可能有用:

if issubclass(type(o), str):

有关相关信息,请参阅Python Library Reference中的Built-in Functions

还有一点需要注意:在这种情况下,如果你使用的是python 2,你可能真的想要使用:

if isinstance(o, basestring):

因为这也会捕获Unicode字符串(unicode不是str的子类; strunicode都是basestring的子类。请注意,{3}中不再存在basestring,其中a strict separation字符串(str)和二进制数据(bytes)。

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

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

答案 1 :(得分:173)

检查对象类型的 Pythonic方法是......不检查它。

由于Python鼓励Duck Typing,你应该try...except以你想要的方式使用对象的方法。因此,如果您的函数正在查找可写文件对象,检查它是file的子类,只是尝试使用其.write()方法!

当然,有时这些漂亮的抽象会崩溃,isinstance(obj, cls)就是你所需要的。但要谨慎使用。

答案 2 :(得分:47)

如果isinstance(o, str)True或者是o继承的类型,则

str将返回str

当且仅当type(o) is str是str时,

True才会返回o。如果False属于继承自o的类型,则会返回str

答案 3 :(得分:22)

在提出问题并回答之后,type hints were added to 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类型,还是任何可迭代的东西?)。

显式类型检查的优点是它可以更早地捕获错误并提供比duck typing更清晰的错误消息。鸭子类型的确切要求只能用外部文档表示(希望它是彻底和准确的),不兼容类型的错误可能远离它们的起源。

Python的类型提示旨在提供一种折衷方案,可以指定和检查类型,但在通常的代码执行过程中没有额外的成本。

typing包提供了类型变量,可以在类型提示中使用,以表达所需的行为,而无需特定类型。例如,它包含IterableCallable等变量,用于指定对具有这些行为的任何类型的需求。

虽然类型提示是检查类型的最Pythonic方式,但通常更不用Pythonic来检查类型并依赖于duck typing。类型提示相对较新,当他们是最恐怖的解决方案时,陪审团仍在继续。一个相对无争议但非常一般的比较:类型提示提供了一种可以强制执行的文档形式,允许代码生成更早且更容易理解的错误,可以捕获鸭子不能输入的错误,并且可以静态检查(在不寻常的情况下)感觉但它仍然在运行时之外)。另一方面,鸭子打字长期以来一直是Pythonic方式,不会强加静态类型的认知开销,不那么冗长,并且会接受所有可行的类型然后一些。

答案 4 :(得分:17)

这是一个例子,为什么鸭子打字是邪恶的,而不知道它何时是危险的。 例如:这是Python代码(可能省略了正确的缩进),请注意这一点 通过照顾isinstance和issubclassof函数可以避免这种情况,以确保当你真的需要一只鸭子时,你就不会得到炸弹。

class Bomb:
    def __init__(self):
        ""

    def talk(self):
        self.explode()

    def explode(self):
        print "BOOM!, The bomb explodes."

class Duck:
    def __init__(self):
        ""
    def talk(self):
        print "I am a duck, I will not blow up if you ask me to talk."    

class Kid:
    kids_duck = None

    def __init__(self):
        print "Kid comes around a corner and asks you for money so he could buy a duck."

    def takeDuck(self, duck):
        self.kids_duck = duck
        print "The kid accepts the duck, and happily skips along"

    def doYourThing(self):
        print "The kid tries to get the duck to talk"
        self.kids_duck.talk()

myKid = Kid()
myBomb = Bomb()
myKid.takeDuck(myBomb)
myKid.doYourThing()

答案 5 :(得分:12)

isinstance(o, str)

Link to docs

答案 6 :(得分:6)

我认为使用像Python这样的动态语言很酷,你真的不应该检查这样的东西。

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

我在使用urllib2.urlopen()从网上获取数据时使用了很多内容,而read()会返回文件,如对象。反过来,这可以传递给几乎任何从文件中读取的方法,因为它实现了与真实文件相同的isinstance()方法。

但我确信有时间和地点可以使用{{1}},否则它可能不存在:)

答案 7 :(得分:4)

到雨果:

您可能意味着list而不是array,但这指向了类型检查的整个问题 - 您不想知道相关对象是否是列表,您想知道如果它是某种序列或者它是一个单一的对象。所以尽量像序列一样使用它。

假设您要将对象添加到现有序列中,或者如果它是一系列对象,则将它们全部添加

try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

这样做的一个技巧是如果你正在使用字符串和/或字符串序列 - 这很棘手,因为字符串通常被认为是单个对象,但它也是一系列字符。更糟糕的是,因为它实际上是一系列单长度字符串。

我通常选择设计我的API,使其只接受单个值或序列 - 它使事情变得更容易。如果需要,将[ ]传递给单个值并不难。

(虽然这会导致字符串错误,因为它们看起来像是序列。)

答案 8 :(得分:1)

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

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

<块引用>

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

<块引用>

A:使用 isinstance, issubclass, type 根据类型进行检查。

然而,正如其他答案和评论很快指出的那样,“类型检查”的想法比 python 中的要多得多。自添加 Python 3 和 type hints 以来,也发生了很大变化。下面,我将讨论类型检查、鸭子类型和异常处理方面的一些困难。对于那些认为不需要类型检查的人(通常不需要,但我们在这里),我还指出了如何使用类型提示来代替。

类型检查

在 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. 事先检查,但在我们检查时存储所有内容。

选项 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 并能够添加 { {1}})。

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

  1. n 不是 float,但它满足行为工作要求。

  2. 用 try-except 块包装整个代码也是不好的做法。

起初这些似乎不是问题,但这里有一些可能会改变您想法的原因。

  1. 用户不能再期望我们的函数按预期返回 int。这可能会在其他地方破坏代码。

  2. 由于异常的来源多种多样,因此在整个代码块上使用 try-except 可能最终会捕获您不打算捕获的异常。我们只想检查 int 是否可迭代并且是否有整数元素。

  3. 理想情况下,我们希望在我们的代码生成器中捕获异常并在它们的位置上引发更多信息丰富的异常。当从其他人的代码中引发异常时,除了您没有编写的一行之外没有任何解释并且发生了一些 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

以下是使用类型提示的一些优势。

  • 代码现在看起来不错!

  • 如果您使用类型提示,您的编辑器可能会执行静态类型分析!

  • 它们存储在函数/类中,使它们动态可用,例如typeguarddataclasses

  • 它们在使用 from typing import Iterable def sum(nums: Iterable[int]) -> int: result = 0 for n in nums: result += n return result 时出现在函数中。

  • 无需根据描述判断您的输入类型是否正确,或者更糟的是缺少描述。

  • 您可以根据 structure 例如“输入”提示“有这个属性吗?”无需用户进行子类化。

类型提示的缺点?

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

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

答案 10 :(得分:1)

python 3.10中,您可以在|中使用isinstance

>>> isinstance('1223', int | str) 
True

>>> isinstance('abcd', int | str) 
True

答案 11 :(得分:0)

您可以使用类型的__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'

答案 12 :(得分:0)

检查类型的简单方法是将其与您知道的类型进行比较。

>>> a  = 1
>>> type(a) == type(1)
True
>>> b = 'abc'
>>> type(b) == type('')
True

答案 13 :(得分:-1)

我认为最好的方法是正确键入变量。您可以通过使用“ typing”库来做到这一点。

示例:

from typing import NewType UserId = NewType ('UserId', int) some_id = UserId (524313)`

请参见https://docs.python.org/3/library/typing.html

答案 14 :(得分:-6)

您可以使用以下行检查以检查给定值的字符类型:

def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)