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


1276

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

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


7
好吧,Python中的规范方法是根本不检查类型(除非您正在调试)。通常,您只是尝试将其用作字符串(例如,与其他字符串连接,打印到控制台等);如果您认为它可能会失败,请使用try / except或hasattr。就是说,可接受的答案是在Python世界中完成您通常“不应做”的规范方法。欲了解更多信息,谷歌“的Python鸭打字”或阅读这些:voidspace.org.uk/python/articles/duck_typing.shtml stackoverflow.com/questions/610883/...
乔恩·库姆斯

9
我认为Coombs先生正在忽略非JSON可序列化类之类的示例。如果将大量数据通过一个函数(其代码无法影响)放置,则可能需要在传递数据之前将某些数据转换为例如<str>。至少那是最终在此页面上看到的方式...
John Carrell

2
要求这样做的最常见原因似乎是要区分字符串和字符串的可迭代对象。这是一个棘手的问题,因为字符串字符串可迭代对象-单字符字符串甚至是其自身的序列(上次我检查过-一个可能不应该依赖它)。但是,有人会用类似字符串的东西吗?是的。因此,“我应该怎么做才能区分字符串和其他可迭代字符串”的答案?是正确的:“这取决于您要做什么”。:-D
clacke

2
现在,Python类型注释已成问题。看看mypy
Sheena

Answers:


1522

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

if isinstance(o, str):

要检查的类型o是否准确str(排除子类):

if type(o) is str:

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

if issubclass(type(o), str):

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

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

if isinstance(o, basestring):

因为这也将赶上Unicode字符串(unicode不是的子类str,这两个strunicode是的子类basestring)。请注意,basestring在Python 3中不再存在,在Python 3中,字符串()和二进制数据()严格分开strbytes

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

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

31
str .__ subclasses __()仅返回str的直接子类,与issubclass()或isinstance()的作用不同。(要做到这一点,你将不得不递归调用.__子类__()。
托马斯·沃特斯

15
这是一个很好的答案,但是我认为它确实应该从警告您通常不应该在Python中执行此操作开始。实际上,似乎可以验证这样的假设:这是“在Python中要做的规范的事情”,但事实并非如此。
乔恩·库姆斯

4
这些是python2的答案。例如,python3中没有basestring。
dfrankow

4
实例和“完全”之间有什么区别?如果type(a) is Object不是这样,那也是真的isinstance(a, Object)。但是,如果type(a) is SubClassOfObject,则 type(a) is Object == False,但是isinstance(a, Object) == True。对?
mavavilj

1
@mavavilj- a is b表示a和b完全相同,即在内存中引用相同的实体。因此,ab一样,它必须是完全相同的类,而不是子类isinstance()。参见例如stackoverflow.com/a/133024/1072212
Terry Brown

196

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

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

当然,有时这些漂亮的抽象会分解,isinstance(obj, cls)这正是您所需要的。但是要谨慎使用。


75
恕我直言,最Python化的方式是应付给出的任何参数。在我的代码中,我常常不知道我是接收一个对象还是对象数组,并且在内部使用类型检查将单个对象转换为一个元素列表。
sastanin

14
有时只是尝试使用它的write方法,有时您希望这样做而不会引起异常。在这种情况下,您可以... if hasattr(ob, "write") and callable(ob.write): 或保存一些dict权限...func = getattr(ob, "write", None) if callable(func): ...
ideaman42

142
鸭子打字是关于使用库的。类型检查与编写库有关。问题域不一样。
RickyA 2012年

16
@RickyA,我不同意。鸭子类型化是关于使用具有众所周知语义的接口与对象进行交互。这可以应用于库代码或使用该库的代码。
Dan Lenski 2014年

6
@ nyuszika7h,在Python3中hasattr仅禁止AttributeError-参见:docs.python.org/3.4/library/functions.html#hasattr
ideaman42 2014年

57

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

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


6
当然,如果对象不是'str'的实例,而是类似字符串的实例,这将失败。像unicode,mmap,UserString或任何其他用户定义的类型。Python中通常的方法是不进行类型检查。
Thomas Wouters

6
您不必道歉,可以回答您自己的问题。SO是答案,而不是业障。
伊莱·班德斯基

2
这非常有帮助。因为之间的差异isinstancetype(var) == type('')不明确。
萨斯坦宁

30

在询问并回答问题之后,将类型提示添加到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的解决方案时还没有定论。相对无争议的但非常笼统的比较:类型提示提供了一种可以强制执行的文档形式,允许代码生成更早且更容易理解的错误,可以捕获鸭子输入不能的错误,并且可以静态检查(在不寻常的情况下)感觉,但它仍在运行时之外)。另一方面,鸭子类型很长一段时间以来一直是Python的方式,不会增加静态类型的认知开销,不那么冗长,并且会接受所有可行的类型,然后接受某些类型。


2
-1:mypy特别称自己为“静态类型检查器”,因此我不确定从何处获得“必须在运行时进行类型检查”。
凯文(Kevin)

@Kevin回想起来,这是不必要的题外话,但是要深入了解它,Python的类型提示将转换为运行时数据,并且mypyimportlib用于访问该数据的Python模块。这是否是“静态类型检查”是一个哲学问题,但是它与大多数人所期望的不同,因为涉及到正常的语言解释器和导入机制。
Praxeolitic

4
也不是。它使用 typed_ast,它本身只是具有附加功能的ast的克隆ast不导入模块;它将它们解析为抽象语法树。
凯文(Kevin)

18

这是一个为什么鸭式打字是邪恶的却不知道什么时候是危险的例子。例如:这是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()

36
即使进行类型检查,您也可以创建class EvilDuck(Duck)和覆盖talk()。或更可能的是,class ChineseCancerDuck(Duck)这种讨厌的副作用要到几年后才会显现出来。您最好监督孩子(并彻底测试她的玩具:)
Brett Thomas

36
炸弹不说话。不要添加无意义的方法,这不会发生。
2014年

7
@Dmitry,这是对Duck Typing的普遍批评:en.wikipedia.org/wiki/Duck_typing#Criticism ...您基本上是在说,语言没有强制其语义的任何接口都是邪恶的。我相信这更多是Java的方法。Python的鸭子输入法的全部要点是,只有在普遍支持特定接口含义的约定时,它才有效。例如,您可以通过覆盖__file__属性(通常用于标识类似文件的对象)来表示其他含义,从而浪费大量的Python代码。
Dan Lenski 2014年

2
这一切都归结为一个古老的笑话“医生,当我这样做时会很痛。” ...“那就不要那样做。” 对于习惯于“如果它可以编译,它就可以运行”的人来说,这并不令人满意,但这就是为什么测试迷恋源于动态语言世界。
clacke

1
@clacke基本上,在运行时强制类型严格来说太昂贵了,因为EVERYTHING必须是一个对象(以便从字符串映射到任何可能的类型),并且太方便而没有鸭嘴式,因为鸭嘴式允许使用真正强大的原型技术来克服那些刚性接口通常很难做到。此外,任何静态语言都面临着需要通过动态库,评估和字符串化或接口来创建鸭子类型的问题,而这些本质上并没有使其变得邪恶,只是非常强大。
德米特里


7

我认为使用像Python这样的动态语言的很酷的事情是,您实际上不必检查类似的东西。

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

当从网络上获取数据urllib2.urlopen()并返回类似对象的文件时,我已经使用了很多。这又可以传递给几乎所有从文件读取的方法,因为它实现了相同的方法。read()方法与真实文件。

但我确定会有时间和地点使用isinstance(),否则可能不会存在:)


关于何时必须使用它的一个很好的例子是,如果您正在分析动态json对象。您不会提前知道字段是字符串还是字典。
灰色,

6

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

6

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

谢谢,这是我在向用户显示反馈时想要的密码。花了我太长时间才找到这个...
Aaron D. Marasco

5

前往雨果:

你可能是说list而不是array,但这指向类型检查的整个问题-您不想知道所讨论的对象是否是列表,您不想知道它是某种序列还是单个对象。因此,请尝试像序列一样使用它。

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

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

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

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

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


0

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

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


-7

您可以检查以下行,以检查给定值是哪种字符类型:

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

chr_type("12)

3
您确定不想删除此答案@Venkatesan吗?
灰色,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.