在Python中检查类型的标准方法是什么?

What's the canonical way to check for type in Python?

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

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


要检查ostr的实例还是str的任何子类,可以使用isinstance(这是"规范的"方法):

1
if isinstance(o, str):

检查o的类型是否为str(不含子类):

1
if type(o) is str:

以下方法也有效,在某些情况下可能有用:

1
if issubclass(type(o), str):

有关信息,请参阅Python库参考资料中的内置函数。

还有一点要注意:在这种情况下,如果您使用的是python2,您可能实际上想要使用:

1
if isinstance(o, basestring):

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

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

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


检查对象类型的最python方法是…不去检查。

因为Python鼓励Duck类型,所以应该只使用try...except以您希望的方式使用对象的方法。因此,如果您的函数正在寻找一个可写文件对象,不要检查它是否是file的子类,只要尝试使用它的.write()方法!

当然,有时候这些漂亮的抽象会崩溃,而isinstance(obj, cls)正是您所需要的。但是很少使用。


如果ostr或者是继承自str的类型,则isinstance(o, str)将返回True

当且仅当o是一个str时,type(o) is str将返回True。当o是继承自str的类型时,它将返回False


在询问和回答问题之后,类型提示被添加到Python中。Python中的类型提示允许检查类型,但方法与静态类型语言非常不同。Python中的类型提示将预期的参数类型与函数关联起来,作为与函数关联的运行时可访问数据,这允许检查类型。类型提示语法示例:

1
2
3
4
5
def foo(i: int):
    return i

foo(5)
foo('oops')

在本例中,我们希望为foo('oops')触发一个错误,因为参数的带注释类型是int。当脚本正常运行时,添加的类型提示不会导致错误发生。但是,它将属性添加到描述预期类型的函数中,其他程序可以查询这些类型并使用这些类型来检查类型错误。

可以用来查找类型错误的其他程序之一是mypy:

1
2
mypy script.py
script.py:12: error: Argument 1 to"foo" has incompatible type"str"; expected"int"

(您可能需要从包管理器中安装mypy。我不认为它带有CPython,但它似乎具有某种程度的"官僚化"。

这种类型检查与静态类型编译语言中的类型检查不同。因为在Python中类型是动态的,所以必须在运行时进行类型检查,如果我们坚持每次都要进行类型检查,那么即使是正确的程序也要付出代价。显式类型检查也可能比需要的限制更多,并导致不必要的错误(例如,参数是否确实需要精确为list类型,或者任何可迭代的内容是否足够?)

显式类型检查的优点是,它可以更早地捕获错误,并提供比duck类型更清晰的错误消息。duck类型的确切需求只能通过外部文档来表达(希望它是全面和准确的),而且来自不兼容类型的错误可能会远离它们的起源。

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

typing包提供了类型变量,可以在类型提示中使用这些类型变量来表达所需的行为,而不需要特定的类型。例如,它包含IterableCallable等变量作为提示,以指定使用这些行为的任何类型的需要。

虽然类型提示是检查类型的最符合python风格的方法,但是完全不检查类型并依赖duck类型通常更符合python风格。类型提示是一种相对较新的解决方案,它何时是最符合python风格的解决方案还没有定论。一个相对没有争议但非常普遍的比较是:类型提示提供了一种可以强制执行的文档形式,允许代码生成更早、更容易理解的错误,可以捕获无法避免输入的错误,并且可以静态检查(在某种特殊的意义上,它仍然在运行时之外)。另一方面,duck类型很长一段时间以来一直是python式的方法,它不增加静态类型的认知开销,也不那么冗长,并且可以接受所有可行的类型,然后再接受一些。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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()


1
isinstance(o, str)

链接到文档


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

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

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

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


雨果:

您的意思可能是list而不是array,但这指出了类型检查的整个问题——您不想知道所讨论的对象是一个列表,而是想知道它是某种序列还是单个对象。所以试着把它当做一个序列来使用。

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

1
2
3
4
try:
   my_sequence.extend(o)
except TypeError:
  my_sequence.append(o)

其中一个技巧是,如果您正在处理字符串和/或字符串序列——这很棘手,因为字符串通常被认为是单个对象,但它也是字符序列。更糟的是,它实际上是一个单长度字符串序列。

我通常选择设计我的API,使它只接受单个值或序列——这使事情变得更容易。如果需要,在传入单个值时,在其周围放置一个[ ]并不难。

(尽管这可能会导致字符串的错误,因为它们看起来确实像(are)序列。)


对于更复杂的类型验证,我喜欢typeguard基于python类型提示注释的验证方法:

1
2
3
4
5
6
7
from typeguard import check_type
from typing import List

try:
    check_type('mylist', [1, 2], List[int])
except TypeError as e:
    print(e)

您可以以非常干净和可读的方式执行非常复杂的验证。

1
2
3
check_type('foo', [1, 3.14], List[Union[int, float]])
# vs
isinstance(foo, list) and all(isinstance(a, (int, float)) for a in foo)

你可以用下面的行来检查给定值的字符类型:

1
2
3
4
5
6
7
8
9
def chr_type(chrx):
    if chrx.isalpha()==True:
        return 'alpha'
    elif chrx.isdigit()==True:
        return 'numeric'
    else:
        return 'nothing'

chr_type("12)