如何告诉一个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中的行为已经改变,因为StringTypes
和basestring
不再被定义。 根据你的需要,你可以用例如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.py
( collections.py
一个实现细节)中定义的Iterable
的精确副本(只更改类名)…这个原因是你希望的,而collections.Iterable
不是这个原因后者通过在这个class
声明之后显式地调用Iterable.register(str)
来确保string被认为是可迭代的。
当然,在any
你希望从你的定义中排除的类的调用之前返回False
,很容易增加__subclasshook__
。
在任何情况下,在将这个新模块导入为myiter
, isinstance('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.