确定对象的类型?


1790

有没有一种简单的方法来确定变量是列表,字典还是其他?我回来的对象可能是任何一种类型,我需要能够分辨出两者之间的区别。


44
虽然我总体上同意您的观点,但在某些情况下了解它会有所帮助。在这种情况下,我进行了一些快速的黑客攻击,最终又回滚了,所以这次您是正确的。但是在某些情况下-例如,在使用反射时-重要的是要知道要处理的对象类型。
贾斯汀·埃斯蒂尔2010年

67
@ S.Lott我不同意;通过了解类型,您可以处理一些漂亮的变体输入,并且仍然可以做正确的事情。它使您可以解决依赖于纯鸭子类型固有的接口问题(例如,Tree上的.bark()方法与Dog上的含义完全不同)。例如,您可以使函数在接受字符串(例如,路径),路径对象或列表的文件。所有人都有不同的接口,但最终结果是相同的:对该文件进行一些操作。
罗伯特·P

22
@ S.Lott我希望这显然是一个人为的例子;但是,这是鸭子输入的主要失败点,并且try没有帮助。例如,如果您知道用户可以传递字符串或数组,则两者都可以索引,但是该索引意味着完全不同的东西。在这些情况下,仅依靠try-catch会以意外和奇怪的方式失败。一种解决方案是制作一个单独的方法,另一种解决方案是添加一些类型检查。我个人更喜欢多态行为,而不是做几乎相同事情的多种方法……但这就是我自己:)
Robert P

22
@ S.Lott,单元测试如何?有时,您希望您的测试来验证函数是否返回了正确的类型。一个非常真实的例子是您拥有班级工厂。
Elliot Cameron 2012年

17
对于一个不太人为的示例,请考虑一个串行器/解串器。根据定义,您正在用户提供的对象和序列化表示之间进行转换。序列化程序需要确定您传入的对象的类型,并且可能没有足够的信息来确定反序列化类型,而无需询问运行时(或者至少,您可能需要使用它进行健全性检查,以便在输入错误数据之前捕获不良数据)您的系统!)
卡尔(Karl

Answers:


1975

有两个内置函数可以帮助您识别对象的类型。您可以使用type() ,如果你需要一个对象的确切类型,并isinstance()检查对象的反对的东西类型。通常,您希望使用isistance()大多数时间,因为它非常健壮并且还支持类型继承。


要获取对象的实际类型,请使用内置type()函数。将对象作为唯一参数传递将返回该对象的类型对象:

>>> type([]) is list
True
>>> type({}) is dict
True
>>> type('') is str
True
>>> type(0) is int
True

当然,这也适用于自定义类型:

>>> class Test1 (object):
        pass
>>> class Test2 (Test1):
        pass
>>> a = Test1()
>>> b = Test2()
>>> type(a) is Test1
True
>>> type(b) is Test2
True

请注意,type()这只会返回对象的直接类型,而不能告诉您类型继承。

>>> type(b) is Test1
False

为此,您应该使用该isinstance功能。当然,这也适用于内置类型:

>>> isinstance(b, Test1)
True
>>> isinstance(b, Test2)
True
>>> isinstance(a, Test1)
True
>>> isinstance(a, Test2)
False
>>> isinstance([], list)
True
>>> isinstance({}, dict)
True

isinstance()通常是确保对象类型的首选方法,因为它还将接受派生类型。因此,除非您实际需要类型对象(无论出于何种原因),否则使用isinstance()优先于type()

第二个参数isinstance()还接受类型的元组,因此可以一次检查多个类型。isinstance如果对象属于以下任何类型,则将返回true:

>>> isinstance([], (tuple, list, set))
True

68
我认为使用更简单is==因为类型是单例
John La Rooy

18
@gnibbler,isinstance无论如何都是首选形式,在这种情况下,您应该进行类型检查(一开始就不应该这样做),因此都不使用==is不需要使用。
Mike Graham 2010年

23
@Mike Graham,有时候type是最好的答案。有时候isinstance,最好的答案是有时候,而鸭式的打字是最好的答案。重要的是要了解所有选项,以便您可以选择最适合这种情况的选项。
约翰·拉鲁伊

6
@gnibbler,可能是这样,尽管我还没有遇到过type(foo) is SomeType比更好的情况isinstance(foo, SomeType)
Mike Graham 2010年

5
@poke:我完全同意PEP8,但是您在这里攻击一个稻草人:Sven的论点的重要部分不是PEP8,但是您也可以将其isinstance用于用例(检查类型范围),并使用以及干净的语法,它具有捕获子类的巨大优势。有人OrderedDict会讨厌您的代码失败,因为它只接受纯字典。
飞羊

165

您可以使用type()

>>> a = []
>>> type(a)
<type 'list'>
>>> f = ()
>>> type(f)
<type 'tuple'>

40

使用try... except块可能更Pythonic 。这样一来,如果你有这叫声也像列表,或叫声也像字典类,它会循规蹈矩,无论什么的类型真的是。

为了明确起见,“告诉变量类型”之间的差异的首选方法是使用“ 鸭子类型”:只要变量响应的方法(和返回类型)是子例程所期望的,则将其视为期望的成为。例如,如果您有一个用getattr和重载方括号运算符的类setattr,但使用了一些有趣的内部方案,那么如果它试图模仿的话,它就适合充当字典。

type(A) is type(B)检查的另一个问题是,如果A是的子类B,它将以false编程方式求值,您希望它是的时间true。如果对象是列表的子类,则它应像列表一样工作:检查其他答案中提供的类型将防止此情况。(isinstance但是可以)。


16
鸭子打字并不是说出真正的区别。它是关于使用通用接口的。
贾斯汀·埃斯蒂尔

5
注意-大多数编码样式指南建议不要将异常处理用作代码的常规控制流的一部分,通常是因为这样会使代码难以阅读。当您要处理错误时,try... except是一个很好的解决方案,但是在根据类型决定行为时却不是。
Rens van der Heijden

34

在对象的实例上,您还具有:

__class__

属性。这是从Python 3.3控制台获取的示例

>>> str = "str"
>>> str.__class__
<class 'str'>
>>> i = 2
>>> i.__class__
<class 'int'>
>>> class Test():
...     pass
...
>>> a = Test()
>>> a.__class__
<class '__main__.Test'>

请注意,在python 3.x和New-Style类(可从Python 2.6中可选)中,类和类型已合并,这有时会导致意外结果。主要是因为这个原因,我最喜欢的测试类型/类的方法是内置函数中的isinstance


2
最后一点很重要。type(obj)是Class不能正常工作,但是isinstance可以解决问题。我知道无论如何还是要首选isinstance,但比接受的答案中建议的那样,它比检查派生类型更有益。
mstbaum

__class__在Python 2.x上大多数情况下都可以,在Python中唯一没有__class__属性的对象是老式类AFAIK。顺便说一下,我不理解您对Python 3的关注-在这样的版本上,只有每个对象都有一个__class__指向正确类的属性。
艾伦·弗朗佐尼

21

确定Python对象的类型

确定对象的类型 type

>>> obj = object()
>>> type(obj)
<class 'object'>

尽管可行,但请避免使用双下划线属性,例如__class__-它们在语义上不公开,并且在这种情况下(也许不是),内置函数通常具有更好的行为。

>>> obj.__class__ # avoid this!
<class 'object'>

类型检查

有没有一种简单的方法来确定变量是列表,字典还是其他?我回来的对象可能是任何一种类型,我需要能够分辨出两者之间的区别。

嗯,这是一个不同的问题,不要使用type-use isinstance

def foo(obj):
    """given a string with items separated by spaces, 
    or a list or tuple, 
    do something sensible
    """
    if isinstance(obj, str):
        obj = str.split()
    return _foo_handles_only_lists_or_tuples(obj)

这涵盖了您的用户通过子类来做一些聪明或明智的事情的情况str-根据Liskov Substitution的原理,您希望能够在不破坏代码的情况下使用子类实例-并isinstance支持这一点。

使用抽象

甚至更好的是,您可能会从collections或寻找特定的抽象基类numbers

from collections import Iterable
from numbers import Number

def bar(obj):
    """does something sensible with an iterable of numbers, 
    or just one number
    """
    if isinstance(obj, Number): # make it a 1-tuple
        obj = (obj,)
    if not isinstance(obj, Iterable):
        raise TypeError('obj must be either a number or iterable of numbers')
    return _bar_sensible_with_iterable(obj)

或者只是不明确地进行类型检查

或者,也许最重要的是,使用鸭式输入,而不要显式地检查代码。鸭式打字以更高的雅致和更少的冗长性支持Liskov Substitution。

def baz(obj):
    """given an obj, a dict (or anything with an .items method) 
    do something sensible with each key-value pair
    """
    for key, value in obj.items():
        _baz_something_sensible(key, value)

结论

  • 使用type真正得到一个实例的类。
  • 使用isinstance显式检查实际的子类或注册的抽象。
  • 只是避免在有意义的地方进行类型检查。

总会有try/ except而不是显式检查。
toonarmycaptain

如果用户不确定他们将传入的类型,大概就是用户要做的事情。我不喜欢用异常处理来弄乱正确的实现,除非我能很好地处理异常。引发的异常应该足以通知用户他们需要更正其用法。
亚伦·霍尔

13

您可以使用type()isinstance()

>>> type([]) is list
True

警告您可以list通过在当前作用域中分配相同名称的变量来破坏文件或其他任何类型。

>>> the_d = {}
>>> t = lambda x: "aight" if type(x) is dict else "NOPE"
>>> t(the_d) 'aight'
>>> dict = "dude."
>>> t(the_d) 'NOPE'

在上方,我们看到dict将其重新分配给字符串,因此进行了测试:

type({}) is dict

...失败。

要解决此问题并type()谨慎使用:

>>> import __builtin__
>>> the_d = {}
>>> type({}) is dict
True
>>> dict =""
>>> type({}) is dict
False
>>> type({}) is __builtin__.dict
True

2
我不确定是否有必要指出,隐藏内置数据类型的名称对这种情况不利。您的dict字符串也会因其他许多代码而失败,例如dict([("key1", "value1"), ("key2", "value2")])。这类问题的答案是“然后不要那样做”。不要遮盖住内置类型名称,不要期望一切正常。
Blckknght 2014年

3
我同意您的“不要那样做”部分。但是确实要告诉某人不要做某事,您至少应该解释为什么不做,我认为这是这样做的一个相关机会。我的意思是谨慎的方法看起来丑陋,并说明为什么他们可能不想这样做,让他们自己决定。
deed02392 2014年

对于经典实例,type()不能在Python 2.x上正常工作。
艾伦·弗朗佐尼

5

尽管问题已经很老了,但我偶然发现了这个问题,同时自己找到了正确的方法,并且我认为仍然需要澄清一下,至少对于Python 2.x(没有检查Python 3,但是由于经典类出现了问题,在这样的版本上消失了,可能没有关系)。

在这里,我试图回答标题的问题:如何确定任意对象的类型?在许多评论和答案中,关于使用或不使用isinstance的其他建议也可以,但是我没有解决这些问题。

type()方法的主要问题是,它不适用于旧式实例

class One:
    pass

class Two:
    pass


o = One()
t = Two()

o_type = type(o)
t_type = type(t)

print "Are o and t instances of the same class?", o_type is t_type

执行此代码片段将产生:

Are o and t instances of the same class? True

我认为这不是大多数人所期望的。

这种__class__方法最接近正确性,但是在一种关键情况下不起作用:当传入的对象是旧式(而不是实例!)时,因为这些对象缺少此类属性。

这是我能想到的最小的代码片段,以一致的方式满足了此类合法问题:

#!/usr/bin/env python
from types import ClassType
#we adopt the null object pattern in the (unlikely) case
#that __class__ is None for some strange reason
_NO_CLASS=object()
def get_object_type(obj):
    obj_type = getattr(obj, "__class__", _NO_CLASS)
    if obj_type is not _NO_CLASS:
        return obj_type
    # AFAIK the only situation where this happens is an old-style class
    obj_type = type(obj)
    if obj_type is not ClassType:
        raise ValueError("Could not determine object '{}' type.".format(obj_type))
    return obj_type

5

小心使用isinstance

isinstance(True, bool)
True
>>> isinstance(True, int)
True

但是输入

type(True) == bool
True
>>> type(True) == int
False

3

除了前面的答案外,值得一提的是collections.abc它的存在还包含一些补充鸭类的抽象基类(ABC)。

例如,与其明确地检查某物是否为列表,不如:

isinstance(my_obj, list)

如果您只想查看自己拥有的对象是否允许获取物品,可以使用collections.abc.Sequence

from collections.abc import Sequence
isinstance(my_obj, Sequence) 

如果您对允许获取,设置删除项目(即可序列)的对象非常感兴趣,则可以选择collections.abc.MutableSequence

许多其它的ABC被定义在那里,Mapping对于可以使用的地图,对象IterableCallable,等等。有关这些文件的完整列表,请参见的文档collections.abc



1

在许多实际情况下,而不是使用typeisinstance也可以使用@functools.singledispatch,这是用来定义的通用功能功能实现用于不同类型的同一操作的多个函数构成)。

换句话说,当您具有如下代码时,您将希望使用它:

def do_something(arg):
    if isinstance(arg, int):
        ... # some code specific to processing integers
    if isinstance(arg, str):
        ... # some code specific to processing strings
    if isinstance(arg, list):
        ... # some code specific to processing lists
    ...  # etc

这是一个如何工作的小例子:

from functools import singledispatch


@singledispatch
def say_type(arg):
    raise NotImplementedError(f"I don't work with {type(arg)}")


@say_type.register
def _(arg: int):
    print(f"{arg} is an integer")


@say_type.register
def _(arg: bool):
    print(f"{arg} is a boolean")
>>> say_type(0)
0 is an integer
>>> say_type(False)
False is a boolean
>>> say_type(dict())
# long error traceback ending with:
NotImplementedError: I don't work with <class 'dict'>

另外,我们可以使用抽象类一次覆盖几种类型:

from collections.abc import Sequence


@say_type.register
def _(arg: Sequence):
    print(f"{arg} is a sequence!")
>>> say_type([0, 1, 2])
[0, 1, 2] is a sequence!
>>> say_type((1, 2, 3))
(1, 2, 3) is a sequence!

0

type()是比更好的解决方案isinstance(),特别是在booleans

TrueFalse只是关键字,平均10Python编写的。从而,

isinstance(True, int)

isinstance(False, int)

都回来了True。两个布尔值都是整数的实例。type()但是,它更聪明:

type(True) == int

返回False

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.