如何检查一个对象是列表或元组(但不是字符串)?
如何检查一个对象是列表或元组(但不是字符串)?
为了确定输入是一个list
/tuple
,而不是一个str
,我通常会这样做。因为很多时候,我会遇到一个函数错误地传递了一个str
对象,目标函数会假定lst
实际上是一个list
或tuple
,并执行for x in lst
。
assert isinstance(lst, (list, tuple))
我的问题是:有没有更好的方法来实现这个目的?
记住,在 Python 中我们想要使用“鸭子类型”。所以,任何像列表一样的东西都可以被视为列表。因此,不要检查列表的类型,只需要看它是否像列表那样即可。
但是字符串也像列表一样,而且这通常并不是我们想要的。有时甚至是一个问题!因此,明确检查字符串,但然后使用鸭子类型。
这是我为了好玩写的一个函数。它是 repr()
的一个特殊版本,可以在尖括号('<'、'>')中打印任何序列。
def srepr(arg): if isinstance(arg, basestring): # Python 3: isinstance(arg, str) return repr(arg) try: return '<' + ", ".join(srepr(x) for x in arg) + '>' except TypeError: # catch when for loop fails return repr(arg) # not a sequence so just return repr
整体上这很简洁、优雅。但是那个 isinstance()
检查是干什么的?那有点像黑客攻击。但它是必不可少的。
这个函数在任何像列表一样的东西上递归地调用自己。如果我们没有特别处理字符串,那么它会被视为一个列表,一个字符一个字符地分割开来。但是然后递归调用会尝试将每个字符都视为一个列表——并且它会工作!甚至一个只有一个字符的字符串也像一个列表!函数会继续递归调用自己,直到栈溢出。
像这个函数一样,依赖于每个递归调用分解要做的工作的函数,必须特别处理字符串——因为你不能将字符串分解到一个字符以下的级别,而且甚至一个只有一个字符的字符串也像一个列表。
注意:try/except 是表示我们意图最清晰的方式。但如果这段代码在某种程度上是时间关键的,则我们可能希望用某种测试来查看 arg
是否是一个序列,而不是测试类型,我们应该测试行为。如果它有一个 .strip()
方法,那么它就是一个字符串,因此不要将其视为序列;否则,如果它可以被索引或迭代,那么它就是一个序列:
def is_sequence(arg): return (not hasattr(arg, "strip") and hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")) def srepr(arg): if is_sequence(arg): return '<' + ", ".join(srepr(x) for x in arg) + '>' return repr(arg)
编辑:我最初写上了一个检查 __getslice__()
的代码,但我注意到在 collections
模块的文档中,有趣的方法是 __getitem__()
;这是有道理的,这是你索引一个对象的方式。那似乎比 __getslice__()
更基础,所以我修改了上面的内容。