如何检查一个对象是一个列表或元组(而不是string)?
这是我通常为了确定input是一个list
/ tuple
,而不是str
。 因为很多时候我偶然发现了一个函数错误地通过一个str
对象的错误,并且目标函数for x in lst
只要假定lst
实际上是一个list
或tuple
。
assert isinstance(lst, (list, tuple))
我的问题是:有没有更好的方法来实现这一目标?
我认为
assert not isinstance(lst, basestring)
实际上是你想要的,否则你会错过很多像列表一样的东西,但不是list
或tuple
子类。
请记住,在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
值得特别处理。
毕竟,非str
types(例如,维护一些复杂的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()
这是我做的确保清单。