有没有一种简单的方法来确定变量是列表,字典还是其他?我回来的对象可能是任何一种类型,我需要能够分辨出两者之间的区别。
try
没有帮助。例如,如果您知道用户可以传递字符串或数组,则两者都可以索引,但是该索引意味着完全不同的东西。在这些情况下,仅依靠try-catch会以意外和奇怪的方式失败。一种解决方案是制作一个单独的方法,另一种解决方案是添加一些类型检查。我个人更喜欢多态行为,而不是做几乎相同事情的多种方法……但这就是我自己:)
有没有一种简单的方法来确定变量是列表,字典还是其他?我回来的对象可能是任何一种类型,我需要能够分辨出两者之间的区别。
try
没有帮助。例如,如果您知道用户可以传递字符串或数组,则两者都可以索引,但是该索引意味着完全不同的东西。在这些情况下,仅依靠try-catch会以意外和奇怪的方式失败。一种解决方案是制作一个单独的方法,另一种解决方案是添加一些类型检查。我个人更喜欢多态行为,而不是做几乎相同事情的多种方法……但这就是我自己:)
Answers:
有两个内置函数可以帮助您识别对象的类型。您可以使用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
is
,==
因为类型是单例
isinstance
无论如何都是首选形式,在这种情况下,您应该进行类型检查(一开始就不应该这样做),因此都不使用==
或is
不需要使用。
type
是最好的答案。有时候isinstance
,最好的答案是有时候,而鸭式的打字是最好的答案。重要的是要了解所有选项,以便您可以选择最适合这种情况的选项。
type(foo) is SomeType
比更好的情况isinstance(foo, SomeType)
。
isinstance
用于用例(检查类型范围),并使用以及干净的语法,它具有捕获子类的巨大优势。有人OrderedDict
会讨厌您的代码失败,因为它只接受纯字典。
使用try
... except
块可能更Pythonic 。这样一来,如果你有这叫声也像列表,或叫声也像字典类,它会循规蹈矩,无论什么的类型真的是。
为了明确起见,“告诉变量类型”之间的差异的首选方法是使用“ 鸭子类型”:只要变量响应的方法(和返回类型)是子例程所期望的,则将其视为期望的成为。例如,如果您有一个用getattr
和重载方括号运算符的类setattr
,但使用了一些有趣的内部方案,那么如果它试图模仿的话,它就适合充当字典。
type(A) is type(B)
检查的另一个问题是,如果A
是的子类B
,它将以false
编程方式求值,您希望它是的时间true
。如果对象是列表的子类,则它应像列表一样工作:检查其他答案中提供的类型将防止此情况。(isinstance
但是可以)。
try
... except
是一个很好的解决方案,但是在根据类型决定行为时却不是。
在对象的实例上,您还具有:
__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。
__class__
在Python 2.x上大多数情况下都可以,在Python中唯一没有__class__
属性的对象是老式类AFAIK。顺便说一下,我不理解您对Python 3的关注-在这样的版本上,只有每个对象都有一个__class__
指向正确类的属性。
确定对象的类型 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
而不是显式检查。
您可以使用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
dict
字符串也会因其他许多代码而失败,例如dict([("key1", "value1"), ("key2", "value2")])
。这类问题的答案是“然后不要那样做”。不要遮盖住内置类型名称,不要期望一切正常。
尽管问题已经很老了,但我偶然发现了这个问题,同时自己找到了正确的方法,并且我认为仍然需要澄清一下,至少对于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
除了前面的答案外,值得一提的是collections.abc
它的存在还包含一些补充鸭类的抽象基类(ABC)。
例如,与其明确地检查某物是否为列表,不如:
isinstance(my_obj, list)
如果您只想查看自己拥有的对象是否允许获取物品,可以使用collections.abc.Sequence
:
from collections.abc import Sequence
isinstance(my_obj, Sequence)
如果您对允许获取,设置和删除项目(即可变序列)的对象非常感兴趣,则可以选择collections.abc.MutableSequence
。
许多其它的ABC被定义在那里,Mapping
对于可以使用的地图,对象Iterable
,Callable
,等等。有关这些文件的完整列表,请参见的文档collections.abc
。
通常,您可以从具有类名称的对象中提取字符串,
str_class = object.__class__.__name__
并进行比较
if str_class == 'dict':
# blablabla..
elif str_class == 'customclass':
# blebleble..
在许多实际情况下,而不是使用type
或isinstance
也可以使用@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!