如何检查对象是列表还是元组(但不是字符串)?

Rei*_*ica 426 python types assert list

这是我通常做的,以确定输入是list/ tuple- 而不是a str.因为很多次我偶然发现函数str错误地传递了一个对象的bug ,而目标函数确实for x in lst假设它lst实际上是一个list或者tuple.

assert isinstance(lst, (list, tuple))
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*ood 326

仅在python 2中(不是python 3):

assert not isinstance(lst, basestring)
Run Code Online (Sandbox Code Playgroud)

实际上你想要的是什么,否则你会错过很多像列表一样的东西,但不是list或者的子类tuple.

  • 是的,这是正确的答案.在Python 3中,`basestring`消失了,你只需检查`isinstance(lst,str)`. (90认同)
  • 值得注意的是,这并不能保证`lst`是可迭代的,而原始的(例如int会通过此检查) (49认同)
  • @PeterGibson - 两者的组合将提供有效的,更严格的检查并确保1)lst是可迭代的,2)lst不是字符串.`assert isinstance(lst,(list,tuple))和assert not isinstance(lst,basestring)` (11认同)
  • 你可以像列表一样迭代很多东西,例如`set`,生成器表达式,迭代器.有一些奇特的东西,比如`mmap`,不那么奇特的东西,比如`array`就像列表一样,可能还有很多我已经忘记了. (5认同)
  • 好吧,这个解决方案只检查字符串派生类型,但整数,双精度或任何其他不可迭代类型呢? (4认同)
  • 有趣的是,这已经被接受和提升了很多,因为它实际上并没有完全回答这个问题.任何人来到这里都应该通过@suzanshakya检查答案,以获得更好的答案. (2认同)
  • 使用``assert not isinstance(lst,basestring)``,任何不是字符串的东西都会通过这个测试,比如dicts,floats,int. (2认同)

ste*_*eha 170

请记住,在Python中我们想要使用"duck typing".因此,任何像列表一样的东西都可以被视为列表.因此,不要检查列表的类型,只要查看它是否像列表一样.

但字符串也像列表一样,而且往往不是我们想要的.有时甚至是一个问题!因此,请明确检查字符串,然后使用duck typing.

这是我写的一个有趣的功能.它是一个特殊版本,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
Run Code Online (Sandbox Code Playgroud)

总体来说,这是干净而优雅的.但那isinstance()检查是做什么的呢?那是一种黑客攻击.但这很重要.

此函数以递归方式调用自身,就像列表一样.如果我们没有特别处理字符串,那么它将被视为一个列表,并一次拆分一个字符.但是,递归调用会尝试将每个字符视为一个列表 - 它会起作用!即使是单字符字符串也可以作为列表使用!该函数将继续递归调用自身直到堆栈溢出.

像这样的函数,依赖于每个递归调用分解要完成的工作,必须使用特殊情况字符串 - 因为你不能分解低于单字符字符串级别的字符串,甚至一个字符串-character字符串就像一个列表.

注意:try/ except是表达我们意图的最简洁方式.但是如果这段代码在某种程度上是时间关键的,我们可能想用某种测试来替换它以查看是否arg是一个序列.我们应该测试行为,而不是测试类型.如果它有一个.strip()方法,那就是一个字符串,所以不要把它当成一个序列; 否则,如果它是可索引的或可迭代的,那么它是一个序列:

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

编辑:我最初写了上面的检查,__getslice__()但我注意到在collections模块文档中,有趣的方法是__getitem__(); 这是有道理的,这就是你如何索引一个对象.这似乎比__getslice__()我改变了上述内容更为根本.

  • @stantonk,谢谢你这样说,但我认为在写这篇文章时已经有了一个公认的答案,我真的不希望改变所接受的答案. (2认同)

小智 120

H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.
Run Code Online (Sandbox Code Playgroud)

  • "不允许进行鸭子打字"并不能使答案更容易被接受,特别是考虑到这个答案实际上回答了这个问题. (11认同)
  • 除了它没有像其他评论者指出的那样使用鸭子打字的Python习语(虽然它确实直接和干净地回答了问题). (10认同)
  • 这个答案比其他答案更不可接受,因为它不允许进行鸭子打字,并且在子类化的简单情况下也失败了(典型的例子是namedtuple类). (6认同)
  • 我赞成这个答案,但是`if isinstance(H,(list,tuple)):...`更短更清晰. (4认同)
  • 不会将列表的 **subclass** 视为列表。 (2认同)
  • 备用语法:`if type(H)in [list,tuple]:` (2认同)

suz*_*kya 68

对于Python 2:

import collections.abc

if isinstance(obj, collections.abc.Sequence) and not isinstance(obj, str):
    print("obj is a sequence (list, tuple, etc) but not a string")
Run Code Online (Sandbox Code Playgroud)

对于Python 3:

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

已在版本3.3中更改:将集合抽象基类移动到collections.abc模块.为了向后兼容,它们将继续在此模块中可见,直到3.8版本它将停止工作.

  • 哇!这非常好用,并且比任何其他正确的答案简洁得多.我不知道内置类型继承自`collections.Sequence`,但我测试了它,我发现它们确实如此.`xrange`也是如此.更好的是,这个测试排除了`dict`,它同时包含`__getitem__`和`__iter__`. (4认同)
  • 在Python3中,您可以使用isinstance(obj,str)替换isinstance(obj,basestring),这应该可行. (3认同)
  • 在 Python 3 中,你需要而不是 isinstance(obj, bytes) ...如果你想要一个列表,而不仅仅是枚举字节...... (3认同)
  • 在 Python 3 中,你实际上需要 `而不是 isinstance(obj, (str, collections.abc.ByteString))` (2认同)

Ces*_*sar 31

PHP风格的Python:

def is_array(var):
    return isinstance(var, (list, tuple))
Run Code Online (Sandbox Code Playgroud)

  • @peterhil`str`也有`__getitem__`因此你的检查不排除`str` (7认同)
  • 一个字典也是如此.检查`__getitem__`在这里是不好的建议. (7认同)
  • Python是一种鸭子类型的语言,所以你真的应该检查var是否有属性`__getitem__`.此名称也具有误导性,因为还有阵列模块.var也可以是numpy.ndarray或任何其他类型,它有`__getitem__`.请参阅http://stackoverflow.com/a/1835259/470560以获取正确答案. (6认同)

Rob*_*ney 10

一般来说,迭代对象的函数对字符串以及元组和列表起作用的事实比bug更具特色.你当然可以使用isinstance或打字来检查一个参数,但你为什么要这样做?

这听起来像是一个修辞问题,但事实并非如此.答案为"我为什么要检查参数的类型?" 可能会建议解决真正的问题,而不是感知问题.将字符串传递给函数时,为什么会出现错误?另外:如果将字符串传递给此函数时出现错误,如果将其他非list/tuple iterable传递给它,它是否也是一个错误?为什么或者为什么不?

我认为这个问题最常见的答案可能是编写的开发人员f("abc")期望这个函数表现得好像他们写的那样f(["abc"]).在某种情况下,保护开发人员更有意义,而不是支持迭代字符串中字符的用例.但我首先想到它很长很难.

  • "但我首先想到的很长很难." 我不会.如果函数*应该是*list-y函数,那么是的,它应该对它们进行相同的处理(即,给定一个列表,向后吐出它,类似的东西).但是,如果它是一个函数,其中一个参数可以是一个字符串或一个字符串列表(这是一个非常常见的需要)然后强制开发人员使用该函数*总是*在数组内输入他们的参数似乎是有点多.另外,考虑一下如何处理JSON输入.你肯定想要处理与字符串不同的对象列表. (15认同)

Om *_*ash 7

试试这个是为了提高可读性和最佳实践:

Python2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something
Run Code Online (Sandbox Code Playgroud)

Python3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你.


Moe*_*Moe 5

str对象没有__iter__属性

>>> hasattr('', '__iter__')
False 
Run Code Online (Sandbox Code Playgroud)

所以你可以检查一下

assert hasattr(x, '__iter__')
Run Code Online (Sandbox Code Playgroud)

这也AssertionError将为任何其他非可迭代对象带来好处.

编辑: 正如蒂姆在评论中提到的,这只适用于python 2.x,而不是3.x.

  • 小心:在Python 3中,`hasattr('','__ iter __')`返回'True`.当然,这是有道理的,因为你可以迭代一个字符串. (7认同)

max*_*max 5

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

我对上面的@steveha回答非常感兴趣,这似乎给出了一个例子,其中鸭子打字似乎打破了.关于第二个想法,但是,他的例子表明,鸭子类型是很难适应,但它并没有暗示str值得任何特殊处理.

毕竟,非str类型(例如,维护一些复杂递归结构的用户定义类型)可能导致@steveha srepr函数导致无限递归.虽然这是不太可能的,但我们不能忽视这种可能性.因此,与其特殊的壳体内strsrepr,我们应该澄清一下,我们希望srepr当一个无限递归的结果做.

似乎一种合理的方法就是在srepr当下简单地打破递归list(arg) == [arg].事实上,这将完全解决问题str,没有任何问题isinstance.

然而,一个非常复杂的递归结构可能会导致list(arg) == [arg]永远不会发生的无限循环.因此,虽然上述检查很有用,但还不够.我们需要类似递归深度的硬限制.

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


Ler*_*ang 5

在 tensorflow 中找到了一个名为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))
Run Code Online (Sandbox Code Playgroud)

我已经验证它满足您的需求。