Python:检查对象是否为序列
在Python中,有没有简单的方法来判断某个东西是不是一个序列?我试着这样写:if x is not sequence
,但是Python不认可这个写法。
7 个回答
因为Python遵循鸭子类型,所以一种方法是检查一个对象是否有某些成员(方法)。
一个序列有长度,有一系列的项目,并且支持切片操作 [文档]。所以,它的用法大概是这样的:
def is_sequence(obj):
t = type(obj)
return hasattr(t, '__len__') and hasattr(t, '__getitem__')
# additionally: and hasattr(t, '__setitem__') and hasattr(t, '__delitem__')
这些都是特殊的方法,__len__()
应该返回项目的数量,__getitem__(i)
应该返回一个项目(在序列中是第 i 个项目,但不是用于映射),__getitem__(slice(start, stop, step))
应该返回一个子序列,而 __setitem__
和 __delitem__
则像你预期的那样。这就像是一个约定,但这个对象是否真的遵循这个约定,取决于它是否真的实现了这些方法。
注意,上面的函数对于映射类型(比如 dict
)也会返回 True
,因为映射类型也有这些方法。为了避免这个问题,你可以做一些更复杂的工作:
def is_sequence(obj):
try:
len(obj)
obj[0:0]
return True
except TypeError:
return False
不过大多数情况下,你不需要这样做,只需像对待序列一样使用这个对象,如果出现异常就捕获它。这种方式更符合Python的风格。
对于Python 3和2.6及以上版本,你可以检查一个对象是否是collections.Sequence
的子类:
>>> import collections
>>> isinstance(myObject, collections.Sequence)
True
在Python 3.7中,你必须使用collections.abc.Sequence
(因为collections.Sequence
将在Python 3.8中被移除):
>>> import collections.abc
>>> isinstance(myObject, collections.abc.Sequence)
True
不过,这种方法对那些实现了__len__()
和__getitem__()
但没有(应该有的)继承collections.Sequence
的鸭子类型序列是无效的。不过,它适用于所有内置的Python序列类型,比如列表、元组、字符串等。
虽然所有序列都是可迭代的,但并不是所有可迭代的东西都是序列(比如集合和字典是可迭代的,但不是序列)。检查hasattr(type(obj), '__iter__')
会对字典和集合返回True
。
iter(x)
这个函数会在 x
不能被迭代的时候抛出一个 TypeError
错误。不过,它会“接受”集合和字典,但会“拒绝”其他一些不能被迭代的东西,比如 None
和数字。
另一方面,字符串(大多数应用程序把它当成“单个项目”而不是序列)实际上是序列(所以,任何测试,除非特别处理字符串,否则都会确认它们是序列)。因此,这种简单的检查往往不够用。
在 Python 2.6 及更高版本中,引入了 抽象基类,它们提供了更好的系统支持来进行这种“类别检查”。
>>> import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance((), collections.Sequence)
True
>>> isinstance(23, collections.Sequence)
False
>>> isinstance('foo', collections.Sequence)
True
>>> isinstance({}, collections.Sequence)
False
>>> isinstance(set(), collections.Sequence)
False
你会注意到字符串 仍然 被认为是“一个序列”(因为它们 确实是),但至少你可以把字典和集合排除在外。如果你想把字符串从“序列”的概念中排除,可以使用 collections.MutableSequence
(但这样也会排除元组,元组和字符串一样是序列,但它们是不可变的),或者你可以明确地这样做:
import collections
def issequenceforme(obj):
if isinstance(obj, basestring):
return False
return isinstance(obj, collections.Sequence)
根据个人口味调味,热吃!-)
附言:对于 Python 3,使用 str
代替 basestring
,而对于 Python 3.3 及以上版本,抽象基类如 Sequence
已经移到 collections.abc
中。