如何判断变量是可迭代的但不是字符串

pri*_*stc 74 python

我有一个函数接受一个参数,可以是单个项目或双项目:

def iterable(arg)
    if #arg is an iterable:
        print "yes"
    else:
        print "no"
Run Code Online (Sandbox Code Playgroud)

以便:

>>> iterable( ("f","f") )
yes

>>> iterable( ["f","f"] )
yes

>>> iterable("ff")
no

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

scv*_*lex 44

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

import types
if not isinstance(arg, types.StringTypes):
Run Code Online (Sandbox Code Playgroud)

请注意StringTypes的使用.它确保我们不会忘记一些不起眼的字符串.

从好的方面来说,这也适用于派生的字符串类.

class MyString(str):
    pass

isinstance(MyString("  "), types.StringTypes) # true
Run Code Online (Sandbox Code Playgroud)

此外,您可能想看看上一个问题.

干杯.


注:行为在Python 3的改变StringTypesbasestring不再定义.根据您的需求,您可以取代他们isinstancestr,或子集元组(str, bytes, unicode),例如用于用Cython用户.正如@Theron Luhn所提到的,你也可以使用six.

  • -1,即使输入是数字,函数,类,条件也将是"真". (3认同)
  • 我认为糟糕的做法是因为[鸭子打字](http://en.wikipedia.org/wiki/Duck_typing)原则.作为特定类的成员并不意味着它是可以使用的_only_对象,也不意味着预期的方法可用.但我认为有时你甚至无法推断出该方法的作用,即使它存在,所以`isinstance`可能是唯一的方法. (2认同)
  • 注意:types.StringTypes 在 Python 3 中不可用。由于 py3k 中只有一种字符串类型,我认为`do isinstance(arg, str)` 是安全的。对于向后兼容的版本,请考虑使用 https://pythonhosted.org/six/#six.string_types (2认同)
  • **2017**:此答案不再有效,请参阅 /sf/answers/3102995031/,了解适用于所有 Python 版本的答案。 (2认同)

sor*_*rin 19

截至2017年,这是一个适用于所有Python版本的可移植解决方案:

#!/usr/bin/env python
import collections
import six


def iterable(arg):
    return (
        isinstance(arg, collections.Iterable) 
        and not isinstance(arg, six.string_types)
    )


# non-string iterables    
assert iterable(("f", "f"))    # tuple
assert iterable(["f", "f"])    # list
assert iterable(iter("ff"))    # iterator
assert iterable(range(44))     # generator
assert iterable(b"ff")         # bytes (Python 2 calls this a string)

# strings or non-iterables
assert not iterable(u"ff")     # string
assert not iterable(44)        # integer
assert not iterable(iterable)  # function
Run Code Online (Sandbox Code Playgroud)

  • 假设 2021 年每个人都使用 python3,为什么不消除对 6 的依赖呢?使用“isinstance(arg, str)”代替“isinstance(arg, Six.string_types)” (8认同)
  • 从“collections”导入“Iterable”已在 Python 3.10 中删除。相反,从“collections.abc”导入它。 (4认同)

Ale*_*lli 16

从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
Run Code Online (Sandbox Code Playgroud)

这是完全相同的副本(只改变类名)Iterable中定义_abcoll.py(的实现细节collections.py)...的原因,这可以作为你想,虽然collections.Iterable不,是,后者更是不辞辛劳,以确保字符串通过Iterable.register(str)在此class语句之后显式调用,可以认为是可迭代的.

当然,__subclasshook__通过Falseany调用其他类之前返回来扩展很容易,这些类要从定义中明确排除.

在任何情况下,导入这个新模块之后myiter,isinstance('ciao', myiter.NonStringIterable)将会False,并且isinstance([1,2,3], myiter.NonStringIterable)将会True像您要求的那样 - 在Python 2.6及更高版本中,这被认为是体现此类检查的正确方法...定义一个抽象基类并检查isinstance它.

  • *(...) 在 Python 2.6 及更高版本中,这被认为是体现此类检查的正确方法 (...)* 如何以这种方式滥用众所周知的抽象类概念 *正确的方法* 是超出了我的理解范围。正确的方法是引入一些 *lookslike* 运算符。 (2认同)

xva*_*van 7

通过结合以前的回复,我正在使用:

import types
import collections

#[...]

if isinstance(var, types.StringTypes ) \
    or not isinstance(var, collections.Iterable):

#[Do stuff...]
Run Code Online (Sandbox Code Playgroud)

不是 100% 傻瓜证明,但如果一个对象不是可迭代的,你仍然可以让它通过并退回到鸭子类型。


编辑:Python3

types.StringTypes == (str, unicode)。Phython3 的等效项是:

if isinstance(var, str ) \
    or not isinstance(var, collections.Iterable):
Run Code Online (Sandbox Code Playgroud)

编辑:Python3.3

types.StringTypes == (str, unicode)。Phython3 的等效项是:

if isinstance(var, str ) \
    or not isinstance(var, collections.abc.Iterable):
Run Code Online (Sandbox Code Playgroud)

  • 我收到“DeprecationWarning:自 Python 3.3 起,不推荐使用或导入来自 'collections' 而不是 'collections.abc' 的 ABC,并且在 3.9 中它将停止工作”将 collections.Iterable 更改为 collections.abc.Iterable。它不让我编辑! (2认同)