Python:检查对象是否为序列

96 投票
7 回答
61610 浏览
提问于 2025-04-15 23:20

在Python中,有没有简单的方法来判断某个东西是不是一个序列?我试着这样写:if x is not sequence,但是Python不认可这个写法。

7 个回答

9

因为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的风格。

17

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

106

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

撰写回答