确定对象的类型?

有没有一种简单的方法来确定一个变量是列表、字典还是其他什么?

2085360 次浏览

使用type()

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

有两个内置函数可以帮助你识别对象的类型。如果你需要对象的确切类型,你可以使用#0,如果你需要对象的确切类型,你可以使用#1检查对象的类型来对抗某些东西。通常,大多数时候你想使用isinstance(),因为它非常健壮并且还支持类型继承。


要获取对象的实际类型,您可以使用内置的#0函数。将对象作为唯一参数传递将返回该对象的类型对象:

>>> type([]) is listTrue>>> type({}) is dictTrue>>> type('') is strTrue>>> type(0) is intTrue

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

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

请注意,type()只会返回对象的直接类型,但无法告诉您类型继承。

>>> type(b) is Test1False

要覆盖这一点,您应该使用#0函数。这当然也适用于内置类型:

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

使用tryexcept块可能更Pythonic。这样,如果你有一个类像列表一样嘎嘎叫,或者像字典一样嘎嘎叫,不管它的类型真的是什么,它都会表现正常。

澄清一下,在变量类型之间“区分”的首选方法是使用鸭子打字:只要变量响应的方法(和返回类型)是你的子例程期望的,就像你期望的那样对待它。例如,如果你有一个类用getattrsetattr重载了括号运算符,但使用了一些有趣的内部方案,如果这是它试图模拟的,它就适合表现得像字典一样。

type(A) is type(B)检查的另一个问题是,如果AB的子类,它的计算结果是false,而在编程上,你希望它是true。如果一个对象是列表的子类,它应该像列表一样工作:检查另一个答案中呈现的类型将防止这种情况。(然而,isinstance会起作用)。

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

__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开始)中,类和类型已经合并,这有时会导致意想不到的结果。主要是因为这个原因,我最喜欢的测试类型/类的方法是is实例内置函数。

您可以使用type()isinstance()

>>> type([]) is listTrue

请注意,您可以通过在当前同名范围内分配变量来破坏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 dictTrue>>> dict ="">>> type({}) is dictFalse>>> type({}) is __builtin__.dictTrue

虽然这些问题很老,但我在自己找到正确的方法时偶然发现了这一点,我认为它仍然需要澄清,至少对于Python 2. x(没有检查Python 3,但由于问题出现在经典类上,这些类在这样的版本上消失了,它可能无关紧要)。

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

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 pythonfrom 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 classobj_type = type(obj)if obj_type is not ClassType:raise ValueError("Could not determine object '{}' type.".format(obj_type))return obj_type

除了前面的答案之外,值得一提的是#0的存在,它包含几个补充鸭子类型的抽象基类(ABC)。

例如,而不是显式检查某物是否是列表:

isinstance(my_obj, list)

如果您只对查看您拥有的对象是否允许获取项目感兴趣,您可以使用#0

from collections.abc import Sequenceisinstance(my_obj, Sequence)

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

这里定义了许多其他ABC,Mapping表示可以用作映射的对象,IterableCallable等。所有这些的完整列表可以在留档#3中看到

确定Python对象的类型

使用type确定对象的类型

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

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

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

类型检查

有没有一种简单的方法来确定一个变量是列表、字典还是其他什么?我正在取回一个可能是任一类型的对象,我需要能够区分它们。

这是一个不同的问题,不要使用type-使用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替换的原则,您希望能够在不破坏代码的情况下使用子类实例——isinstance支持这一点。

使用抽象

更好的是,您可以从collectionsnumbers中查找特定的抽象基类:

from collections import Iterablefrom 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-tupleobj = (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替换,更优雅,更少冗长。

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

小心使用实例

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

但类型

type(True) == boolTrue>>> type(True) == intFalse

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

TrueFalse只是Python中表示10的关键字。因此,

isinstance(True, int)

isinstance(False, int)

两者都返回True。两个布尔值都是整数的实例。然而,type()更聪明:

type(True) == int

返回False

一般来说,您可以使用类名从对象中提取字符串,

str_class = object.__class__.__name__

并将其用于比较,

if str_class == 'dict':# blablabla..elif str_class == 'customclass':# blebleble..

在许多实际情况下,除了使用typeisinstance,您还可以使用#2,它用于定义泛型函数由多个函数组成的函数,对不同类型实现相同的操作)。

换句话说,当你有如下代码时,你会想要使用它:

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

下面是它如何工作的一个小例子:

from functools import singledispatch

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

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

@say_type.registerdef _(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.registerdef _(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!

为了完整起见,isexample不适用于不是实例的子类型的类型检查。虽然这很有意义,但没有一个答案(包括接受的答案)涵盖它。为此使用issubclass

>>> class a(list):...   pass...>>> isinstance(a, list)False>>> issubclass(a, list)True
value = 12print(type(value)) # will return <class 'int'> (means integer)

或者你可以做这样的事

value = 12print(type(value) == int) # will return true

使用type()

x='hello this is a string'print(type(x))

输出

<class 'str'>

要仅提取str,请使用此

x='this is a string'print(type(x).__name__)#you can use__name__to find class

输出

str

如果您使用type(variable).__name__,我们可以读取它