如何检查一个对象是一个列表或元组(而不是string)?

这是我通常为了确定input是一个list / tuple ,而不是str 。 因为很多时候我偶然发现了一个函数错误地通过一个str对象的错误,并且目标函数for x in lst只要假定lst实际上是一个listtuple

 assert isinstance(lst, (list, tuple)) 

我的问题是:有没有更好的方法来实现这一目标?

我认为

 assert not isinstance(lst, basestring) 

实际上是你想要的,否则你会错过很多像列表一样的东西,但不是listtuple子类。

请记住,在Python中,我们要使用“鸭子打字”。 所以,任何像列表一样的行为都可以看作一个列表。 所以,不要检查列表的types,只要看看它是否像列表一样。

但是string也像列表一样,而且往往不是我们想要的。 有时甚至是一个问题! 因此,请明确检查一个string,然后使用鸭子打字。

这是我为了好玩而写的一个function。 这是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()是什么检查在那里? 这是一种黑客。 但这是至关重要的。

这个函数以类似列表的formsrecursion地调用它自己。 如果我们没有专门处理这个string,那么它会被当作一个列表来处理,并且一次分割一个字符。 但是,recursion调用会尝试将每个字符视为一个列表 – 它会起作用! 即使是一个字符的string作为一个列表! 该函数将继续自我调用,直到堆栈溢出。

像这样的函数,依靠每个recursion调用来分解要完成的工作,都必须使用特殊string – 因为不能在单string级别之下分解string,甚至不能分解一个stringstring就像一个列表。

注: try / except是expression我们意图的最干净的方式。 但是如果这个代码在某种程度上是时间关键的,那么我们可能需要用某种testing来replace它,看arg是否是一个序列。 而不是testingtypes,我们可能应该testing行为。 如果它有一个.strip()方法,它是一个string,所以不要认为它是一个序列; 否则,如果它是可索引或可迭代的,则是一个序列:

 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__()更基础,所以我改变了上述。

 H = "Hello" if type(H) is list or type(H) is tuple: ## Do Something. else ## Do Something. 
 import collections if isinstance(obj, collections.Sequence) and not isinstance(obj, basestring): print "obj is a sequence (list, tuple, etc) but not a string or unicode" 

对于Python 3,请注意每个文档 :

在版本3.3中更改:将集合的抽象基类移至collections.abc模块。 为了向后兼容,它们也继续在这个模块中可见。

Python风格的Python:

 def is_array(var): return isinstance(var, (list, tuple)) 

一般来说,迭代对象的函数对string以及元组和列表的作用比缺陷更多。 你当然可以使用isinstance或鸭打字来检查一个参数,但为什么你应该?

这听起来像一个修辞问题,但事实并非如此。 “为什么我应该检查参数的types?”的答案 可能会提出解决真正的问题,而不是被认为的问题。 为什么当一个string被传递给函数的时候是个bug? 另外:如果这是一个错误,当一个string传递给这个函数,这是否也是一个错误,如果其他非列表/元组iterable被传递给它? 为什么或者为什么不?

我认为这个问题最常见的答案可能是编写f("abc")开发人员期望函数的行为就好像他们写了f(["abc"]) 。 有可能的情况下,保护开发者自己比支持string中的字符迭代的用例更有意义。 但是我会先考虑一下。

str对象没有__iter__属性

 >>> hasattr('', '__iter__') False 

所以你可以做一个检查

 assert hasattr(x, '__iter__') 

这也会引发任何其他非迭代对象的AssertionError

编辑:正如Tim在评论中提到的,这只会在Python 2.x,而不是3.x

这不是为了直接回答OP,而是想分享一些相关的想法。

我对上面的@steveha回答非常感兴趣,这似乎是鸭子打字似乎打破的一个例子。 然而,他的第二个想法是,他的例子表明鸭子打字很难遵守,但是并不表示str值得特别处理。

毕竟,非strtypes(例如,维护一些复杂的recursion结构的用户定义types)可能导致@steveha srepr函数导致无限recursion。 虽然这是不可能的,但我们不能忽视这种可能性。 因此,我们应该明确一下当无限recursion结果时,我们希望srepr做什么。

看起来,一个合理的方法是简单地打破时间list(arg) == [arg]的recursion。 事实上,这完全可以解决问题,没有任何isinstance

但是,一个非常复杂的recursion结构可能会导致无限循环,其中list(arg) == [arg]永远不会发生。 因此,虽然上述检查是有用的,但这是不够的。 我们需要像recursion深度的硬限制。

我的意思是,如果你打算处理任意的参数types,通过鸭子打字处理str远远比处理你可能(理论上)遇到的更一般的types要容易得多。 因此,如果您觉得需要排除str实例,则应该要求参数是您明确指定的几个types之一的实例。

我在testing中做这个。

 def assertIsIterable(self, item): #add types here you don't want to mistake as iterables if isinstance(item, basestring): raise AssertionError("type %s is not iterable" % type(item)) #Fake an iteration. try: for x in item: break; except TypeError: raise AssertionError("type %s is not iterable" % type(item)) 

未经testing的发电机,我认为如果通过发电机,您将会处于下一个“收益”状态,这可能会使发电机组在下游发生故障。 但是,这又是一个“unit testing”

我在tensorflow中find这样一个叫做is_sequence的函数 。

 def is_sequence(seq): """Returns a true if its input is a collections.Sequence (except strings). Args: seq: an input sequence. Returns: True if the sequence is a not a string and is a collections.Sequence. """ return (isinstance(seq, collections.Sequence) and not isinstance(seq, six.string_types)) 

我已经证实它符合你的需求。

只要做到这一点

 if type(lst) in (list, tuple): # Do stuff 

我倾向于这样做(如果我真的,真的不得不):

 for i in some_var: if type(i) == type(list()): #do something with a list elif type(i) == type(tuple()): #do something with a tuple elif type(i) == type(str()): #here's your string 

如果你有pandas可用,你可以这样做:

 variable = pd.Series(variable).tolist() 

这是我做的确保清单。