题 确定对象的类型?


有一种简单的方法可以确定变量是否是列表,字典或其他内容?我得到的对象可能是任何一种类型,我需要能够分辨出来。


1384
2018-02-08 21:37


起源


虽然总的来说我同意你的意见,但在某些情况下,知道它是有帮助的。在这个特殊情况下,我正在做一些我最终回滚的快速黑客攻击,所以这次你是正确的。但在某些情况下 - 例如,当使用反射时 - 了解您正在处理的对象类型非常重要。 - Justin Ethier
@ S.Lott我不同意这一点;通过能够知道类型,你可以处理一些漂亮的变体输入,仍然做正确的事情。它让你可以解决依赖纯粹鸭子类型所固有的界面问题(例如,树上的.bark()方法意味着与Dog完全不同的东西。)例如,你可以创建一个可以完成一些工作的函数接受字符串(例如,路径),路径对象或列表的文件。所有都有不同的接口,但最终结果是相同的:对该文件执行一些操作。 - Robert P
@ S.Lott我希望很明显这是一个人为的例子;尽管如此,这是鸭子打字的一个主要失败点,而且是一个 try 无济于事。例如,如果您知道用户可以传入字符串或数组,则两者都可以索引,但该索引意味着完全不同的东西。在这些情况下,简单地依靠try-catch将以意想不到的和奇怪的方式失败。一种解决方案是制作单独的方法,另一种解决方案是添加一点类型检查。我个人更喜欢多态行为而不是多种方法做几乎相同的事情......但那只是我:) - Robert P
@ S.Lott,单元测试怎么样?有时您希望测试验证函数是否返回了正确类型的函数。一个非常真实的例子就是你有班级工厂。 - Elliot Cameron
对于一个不太习惯的例子,考虑一个串行器/解串器。根据定义,您将在用户提供的对象和序列化表示之间进行转换。序列化程序需要确定您传入的对象的类型,并且您可能没有足够的信息来确定反序列化类型而不询问运行时(或者至少,您可能需要它来进行健全性检查以在错误数据进入之前捕获它们你的系统!) - Karl


答案:


要获取对象的类型,可以使用内置的 type() 功能。将对象作为唯一参数传递将返回该对象的类型对象:

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

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

>>> 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

1608
2018-02-08 21:40



我觉得使用起来更清楚 is 代替 == 因为类型是单身人士 - John La Rooy
@gnibbler,在你遇到typechecking的情况下(你不应该开始做), isinstance 无论如何是首选的形式,所以都不是 == 要么 is 需要使用。 - Mike Graham
@Mike格雷厄姆,有时候 type 是最好的答案。有时候 isinstance 是最好的答案,有时鸭打字是最好的答案。了解所有选项非常重要,这样您就可以选择哪种更适合这种情况。 - John La Rooy
@gnibbler,可能是,虽然我还没有遇到过这种情况 type(foo) is SomeType 会比。更好 isinstance(foo, SomeType)。 - Mike Graham
@poke:我完全同意PEP8,但你在这里攻击一个稻草人:Sven的论点的重要部分不是PEP8,而是你可以使用 isinstance 对于你的用例(检查一系列类型),以及一个干净的语法,这有很大的优势,你可以捕获子类。有人用 OrderedDict 会讨厌你的代码失败,因为它只接受纯粹的dicts。 - flying sheep


你可以使用 type()

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

142
2018-02-08 21:39





使用a可能更像Pythonic try...except 块。这样,如果你有一个像列表一样嘎嘎叫的类,或者像dict一样嘎嘎叫,它会表现得很好,无论它的类型是什么  是。

为了澄清,变量类型之间“区分”的首选方法是使用所谓的方法 鸭子打字:只要变量响应的方法(和返回类型)是您的子例程所期望的,就像对待它一样对待它。例如,如果您有一个使用括号运算符重载的类 getattr 和 setattr,但是使用了一些有趣的内部方案,如果这是它试图模仿的话,那么它应该像字典一样运行。

另一个问题是 type(A) is type(B) 检查是否 A 是。的子类 B,它评估为 false 什么时候,以编程方式,你希望它会 true。如果一个对象是列表的子类,它应该像列表一样工作:检查另一个答案中显示的类型将阻止这种情况。 (isinstance 但是会工作。


37
2018-02-08 21:43



尽管如此,鸭子打字并不是真正讲述差异。它是关于使用通用接口。 - Justin Ethier
注意 - 大多数编码样式指南建议不要将异常处理作为代码的正常控制流程的一部分,通常是因为它使代码难以阅读。 try... except 当您想要处理错误时,这是​​一个很好的解决方案,但在决定基于类型的行为时则不是。 - Rens van der Heijden


在对象的实例上你也有:

__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 内置功能。


31
2018-06-26 21:38



你最后的观点非常重要。 type(obj)是Class无法正常工作,但isinstance做了伎俩。我知道isinstance无论如何都是首选,但它比仅仅检查派生类型更有利,正如接受的答案中所建议的那样。 - mstbaum
__class__在Python 2.x上是最好的,Python中唯一没有的对象 __class__ 属性是旧式类AFAIK。顺便说一下,我不理解你对Python 3的关注 - 在这样的版本中,每个对象都有一个 __class__ 指向正确类的属性。 - Alan Franzoni


确定Python对象的类型

确定对象的类型 type

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

虽然它有效,但请避免使用双下划线属性 __class__  - 它们在语义上不公开,并且虽然在这种情况下可能不是这样,但内置函数通常具有更好的行为。

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

类型检查

有一种简单的方法可以确定变量是否是列表,字典或其他内容?我得到的对象可能是任何一种类型,我需要能够分辨出来。

那是一个不同的问题,不要使用类型 - 使用 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)

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

或者,也许最重要的是,使用duck-typing,并且不要显式地键入 - 检查您的代码。鸭子打字支持Liskov替换更优雅和更少的冗长。

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 显式检查实际的子类或注册的抽象。
  • 并且只是避免在有意义的地方进行类型检查。

14
2018-03-14 21:12



永远都是 try/except 而不是明确检查。 - toonarmycaptain
据推测,如果用户不确定他们将要传递的类型,用户将会做什么。我不喜欢使用异常处理来混淆正确的实现,除非我有一些与异常非常相关的事情。引发的异常应足以告知用户他们需要更正其用法。 - Aaron Hall♦


您可以使用 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

10
2018-05-31 21:57



我不确定是否有必要指出阴影内置数据类型的名称对于这种情况是不好的。你的 dict 对于许多其他代码,string也会失败,比如 dict([("key1", "value1"), ("key2", "value2")])。这些问题的答案是 “那就不要那样做了”。不要隐藏内置类型名称并期望事情正常工作。 - Blckknght
我同意你的“不要那样做”部分。但实际上要告诉某人不要做某事你至少应该解释为什么不这样做,我认为这是一个相关的机会。我的意思是谨慎的方法看起来丑陋,并说明为什么他们可能不想这样做,让他们决定。 - deed02392
对于经典实例,type()在Python 2.x上无法正常工作。 - Alan Franzoni


虽然这些问题已经很老了,但我偶然发现了这个问题,同时我自己找到了正确的方法,我认为还需要澄清, 至少对于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

4
2018-06-17 08:35