如何告诉一个variables是可迭代的,但不是一个string

我有一个函数,它可以是一个单一的项目或双重项目的参数:

def iterable(arg) if #arg is an iterable: print "yes" else: print "no" 

以便:

 >>> iterable((“f”,“f”))
是

 >>> iterable([“f”,“f”])
是

 >>> iterable(“ff”)
没有

问题是string在技术上是可迭代的,所以当我尝试arg[1]时候,我不能只捕获ValueError。 我不想使用isinstance(),因为这不是很好的做法(或者我被告知)。

使用isinstance(我不明白为什么这是不好的做法)

 import types if not isinstance(arg, types.StringTypes): 

请注意使用StringTypes。 它确保我们不会忘记一些模糊的string。

另一方面,这也适用于派生的string类。

 class MyString(str): pass isinstance(MyString(" "), types.StringTypes) # true 

另外,你可能想看看这个前面的问题 。

干杯。


注意: Python 3中的行为已经改变,因为StringTypesbasestring不再被定义。 根据你的需要,你可以用例如str或者(str, bytes, unicode)一个子集来代替它们,比如Cython用户。 正如@Theron Luhn所说 ,你也可以用six

既然Python 2.6引入了抽象基类, isinstance (在ABCs上使用,而不是具体的类)现在被认为是完全可以接受的。 特别:

 from abc import ABCMeta, abstractmethod class NonStringIterable: __metaclass__ = ABCMeta @abstractmethod def __iter__(self): while False: yield None @classmethod def __subclasshook__(cls, C): if cls is NonStringIterable: if any("__iter__" in B.__dict__ for B in C.__mro__): return True return NotImplemented 

这是在_abcoll.pycollections.py一个实现细节)中定义的Iterable的精确副本(只更改类名)…这个原因是你希望的,而collections.Iterable不是这个原因后者通过在这个class声明之后显式地调用Iterable.register(str)来确保string被认为是可迭代的。

当然,在any你希望从你的定义中排除的类的调用之前返回False ,很容易增加__subclasshook__

在任何情况下,在将这个新模块导入为myiterisinstance('ciao', myiter.NonStringIterable)将为False ,并且isinstance([1,2,3], myiter.NonStringIterable)将为True ,就像您请求 – 在Python 2.6和更高版本中,这被认为是体现这种检查的正确方法…定义一个抽象基类并检查isinstance

我意识到这是一个旧的职位,但认为这是值得join我的方法为互联网的后代。 下面的函数在Python 2和Python 3的大多数情况下似乎适用于我:

 def is_collection(obj): """ Returns true for any iterable which is not a string or byte sequence. """ try: if isinstance(obj, unicode): return False except NameError: pass if isinstance(obj, bytes): return False try: iter(obj) except TypeError: return False try: hasattr(None, obj) except TypeError: return True return False 

这使用内置的hasattr检查(mis)可迭代的非string,当第二个参数不是string或unicodestring时,将引发TypeError

通过结合以前的答复,我使用:

 import types import collections #[...] if isinstance(var, types.StringTypes ) \ or not isinstance(var, collections.Iterable): #[Do stuff...] 

不是100%的傻瓜certificate,但如果一个对象不是一个迭代,你仍然可以让它通过,并回落鸭打字。

截至2017年,这是一个适用于所有版本的Python的便携式解决scheme:

 #!/usr/bin/env python import collections import six def iterable(arg): return isinstance(arg, collections.Iterable) and not isinstance(arg, six.string_types) x = iterable(("f", "f")) and iterable(["f", "f"]) and not iterable("ff") print(x) 

正如您指出的那样,单个string是一个字符序列。

所以你真正想做的事情是通过使用isinstance或type(a)== str来找出arg是什么样的序列。

如果你想实现一个可变参数的函数,你应该这样做:

 def function(*args): # args is a tuple for arg in args: do_something(arg) 

函数(“ff”)和函数(“ff”,“ff”)将起作用。

我看不到像你这样的可用()函数需要的场景。 isinstance()不是坏样式,而是需要使用isinstance()的情况。

呃,不明白…去了怎么回事

 hasattr( x, '__iter__' ) 

… NB elgehelge在这里评论说,“看看我更详细的答案”,但我找不到他/她的详细的答案

后来

鉴于David Charles对Python3的评论,那么:

 hasattr(x, '__iter__') and not isinstance(x, (str, bytes)) 

? 显然,“basestring”不再是Python3中的一种types:

https://docs.python.org/3.0/whatsnew/3.0.html

 The builtin basestring abstract type was removed. Use str instead. The str and bytes types don't have functionality enough in common to warrant a shared base class.