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


443

这就是我通常做,以确定输入是一个list/ tuple-但不是str。因为很多时候我偶然发现了一个错误,即一个函数str错误地传递了一个对象,而目标函数确实for x in lst假定这lst实际上是一个listor tuple

assert isinstance(lst, (list, tuple))

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


9
type(lst)是list吗?
jackalope 2014年

1
不是isinstance(key,six.string_types)
wyx

Answers:


332

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

assert not isinstance(lst, basestring)

实际上就是您想要的,否则您会错过很多像列表一样的东西,但它们不是listor的子类tuple


91
是的,这是正确的答案。在Python 3中,basestring它消失了,您只需检查即可isinstance(lst, str)
steveha

5
您可以迭代很多事情,例如列表,例如set生成器表达式,迭代器。有类似的异国情调的东西mmap,少有的异国情调的东西array就像列表一样,也许我忘了更多。
尼克·克雷格·伍德

50
值得注意的是,这并不能保证lst迭代是可迭代的,而原始方法却可以保证迭代(例如,一个int会通过这张支票)
Peter Gibson 2012年

11
@PeterGibson-两者的结合将提供有效的,更具限制性的检查,并确保1)lst是可迭代的,2)lst不是字符串。 assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
strongMA

4
好的,此解决方案仅检查字符串派生类型,但是整数,双精度数或任何其他不可迭代的类型呢?
Eneko Alonso

171

请记住,在Python中,我们要使用“鸭子类型”。因此,任何类似列表的行为都可以视为列表。因此,不要检查列表的类型,只看它是否像列表一样。

但是字符串也像列表一样,通常这不是我们想要的。有时甚至是一个问题!因此,显式检查字符串,然后使用鸭子类型。

这是我写的一个有趣的函数。这是它的特殊版本,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()支票在那里做什么?这是一种hack。但这是必不可少的。

该函数以递归方式调用类似于列表的任何对象。如果我们不专门处理字符串,则将其视为列表,并一次拆分一个字符。但是,然后递归调用将尝试将每个字符视为一个列表-它将起作用!即使是一个字符的字符串也可以作为列表!该函数将继续递归调用自身,直到堆栈溢出为止。

像这样的函数,依赖于每个递归调用来分解要完成的工作,必须使用特殊情况的字符串-因为您不能将字符串分解为一个字符以下的字符串,甚至不能分解为一个以下的字符串-字符字符串的作用类似于列表。

注意: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)

编辑:我最初写上面检查,__getslice__()但我注意到在collections模块文档中,有趣的方法是__getitem__(); 这很有意义,这就是您索引对象的方式。这似乎比根本,__getslice__()因此我更改了上面的内容。


2
@stantonk,谢谢您这么说,但是我认为当我写这篇文章时已经有了一个可以接受的答案,而且我真的不希望接受的答案会有所改变。
2012年

@steveha:srepr是一个非常有趣的想法。但是对于是否需要特殊情况,我和您持有不同的意见str。是的,str是迄今为止最明显,最常见的迭代方法,它将导致中的无限递归srepr。但是我可以轻松想象用户定义的可迭代对象的行为方式相同(有或没有充分的理由)。除了特殊情况外str,我们应该承认这种方法可能会遇到无限递归,并同意采取某种处理方式。我将我的建议发布在答案中。
最大

1
我认为这绝对是正确的道路。但是,为了处理特殊情况(在这种情况下为字符串),我认为我们最好问一个问题:“人将如何区分差异?” 例如,考虑一个函数参数,它可以是电子邮件地址列表或单个电子邮件地址(请注意,字符串只是字符列表)。将此变量提供给人类。怎么知道是什么呢?我能想到的最简单的方法是查看列表中每个项目中有多少个字符。如果大于1,则参数肯定不能是字符列表。
乔什

1
我已经考虑了一下,并与其他一些人进行了讨论,我认为一切srepr()都很好。我们需要一个递归函数来处理诸如嵌套在另一个列表中的列表之类的事情。但是对于字符串,我们宁愿将它们打印"foo"<'f', 'o', 'o'>。因此,在此处显式检查字符串很有意义。另外,实际上,没有任何其他数据类型的示例,其中迭代总是返回可迭代的,而递归总是会导致堆栈溢出,因此我们不需要特殊的属性来对此进行测试(“实用性胜过纯度”)。
steveha 2013年

1
这在Python 3中不起作用,因为字符串在Python 3中具有__iter__()方法,但在Python 2中却没有。您在中缺少括号is_sequence(),它应显示为:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
MiniQuark

124
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.

11
除非它没有使用其他评论者指出的鸭子输入的Python习惯用法(尽管它确实直接干净地回答了这个问题)。
索伦·比约恩斯塔德

7
这个答案比其他答案更不可接受,因为它不允许鸭子输入,并且在子类的简单情况下也失败(典型示例是namedtuple类)。
Philippe Gauthier

11
“不允许鸭子打字”不会使答案更难以接受,尤其是考虑到该答案确实回答了问题。
Petri

4
我赞成这个答案,但是if isinstance( H, (list, tuple) ): ...更简短。
shahar_m

2
替代语法:if type(H) in [list, tuple]:
斯特凡辛德勒

77

对于Python 3:

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")

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

对于Python 2:

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"

5
哇!这确实非常好用,并且比其他任何正确答案都简洁得多。我不知道内置类型是从那里继承的,collections.Sequence但是我对其进行了测试,发现它们确实可以继承。也是xrange。更好的是,该测试排除了dict同时具有__getitem__和的测试__iter__
尼尔·梅休

知道为什么结果inspect.getmro(list)不包括在内Sequence吗?isinstancegetmro无法显示所有内容时,我们应该如何弄清楚该怎么办?
史蒂夫·乔根森

@SteveJorgensen方法解析顺序定义了Python用于搜索要在类中使用的正确方法的类搜索路径。Sequence是一个抽象类。
suzanshakya

3
在Python3中,您可以将isinstance(obj,basestring)替换为isinstance(obj,str),这应该可以工作。
阿德里安·凯斯特

2
在Python 3中,您不需要isinstance(obj,bytes)...如果您想要一个东西列表,而不仅仅是枚举字节……
Erik Aronesty

35

具有PHP风格的Python:

def is_array(var):
    return isinstance(var, (list, tuple))

6
Python是一种鸭子式语言,因此您确实应该检查var是否具有attribute __getitem__。这个名称也令人误解,因为还有阵列模块。var也可以是numpy.ndarray或任何其他类型,具有__getitem__。有关正确的答案,请参见stackoverflow.com/a/1835259/470560
peterhil 2012年

9
因此@peterhil str也有__getitem__您的支票不排除str
erikbwork

9
字典也是如此。__getitem__在这里检查是不好的建议。
Petri

10

一般来说,在对象上进行迭代的函数不仅可以处理错误,还可以处理字符串,元组和列表。您当然可以使用isinstance或鸭式输入来检查参数,但是为什么要这么做呢?

这听起来像是个反问,但事实并非如此。答案为“为什么我应该检查参数的类型?” 可能会建议解决实际问题,而不是感知到的问题。将字符串传递给函数时,为什么会出错?另外:如果将字符串传递给此函数是一个错误,是否将其他非列表/元组可迭代传递给它也是一个错误吗?为什么或者为什么不?

我认为这个问题的最常见答案可能是 f("abc")期望该函数的行为就像编写的一样f(["abc"])。在某些情况下,保护开发人员免受自身侵害比支持对字符串中的字符进行迭代的用例更有意义。但是我首先会考虑很长时间。


16
“但是我首先会考虑很长时间。” 我不会 如果该函数应该是 list-y函数,则可以,它们应该被视为相同(即,给定一个列表,将其向后吐出,诸如此类)。但是,如果该函数的参数之一可以是字符串或字符串列表(这是很常见的需求),那么强制使用该函数的开发人员始终将其参数输入到数组内似乎有点。另外,考虑一下如何处理JSON输入。您肯定要对待不同于字符串的对象列表。
Jordan Reiter

8

尝试此操作以提高可读性和最佳做法:

Python2

import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
    # Do something

Python3

import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
    # Do something

希望能帮助到你。


Python 3.6.5:AttributeError: module 'types' has no attribute 'ListType'
Juha Untinen

1
在Python 3中,它是:from typing import List-> isinstance([1, 2, 3], List= Trueisinstance("asd", List)= False
Juha Untinen

5

str对象没有__iter__属性

>>> hasattr('', '__iter__')
False 

所以你可以检查一下

assert hasattr(x, '__iter__')

这也AssertionError将为其他任何不可迭代的对象带来好处。

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


8
注意:在Python 3中,hasattr('','__iter__')return True。当然这是有意义的,因为您可以遍历字符串。
2009年

1
真?我不知道 我一直认为这是解决问题的一种优雅方法,哦。
Moe

1
该测试不适用于pyodbc.Row。它没有iter __(),但它的行为或多或少像一个列表(甚至定义了“ __setitem ”)。您可以很好地迭代其元素。len()函数有效,您可以为其元素建立索引。我正在努力找到适合所有列表类型但不包括字符串的正确组合。我认为我会选择检查“ getitem ”和“ len ”,同时明确排除基字符串。
haridsv 2010年

5

这并不是要直接回答OP,而是要分享一些相关想法。

我对上面的@steveha回答非常感兴趣,这似乎举了一个鸭子输入似乎中断的示例。换个角度说,他的例子表明鸭子的分类很难遵循,但是并不能说明str值得任何特殊处理。

毕竟,非str类型(例如,维护一些复杂的递归结构的用户定义类型)可能导致@steveha srepr函数引起无限递归。尽管这确实不太可能,但我们不能忽略这种可能性。因此,与其特殊外壳strsrepr,我们应该明确,我们想要什么srepr在无限递归产生时的事情情况。

似乎一种合理的方法是srepr暂时中断当前递归list(arg) == [arg]。这,其实,彻底解决这个问题str,没有任何isinstance

但是,真正复杂的递归结构可能会导致无限循环,list(arg) == [arg]永远不会发生。因此,尽管上面的检查很有用,但还不够。我们需要对递归深度进行严格限制。

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


1
嗯,我喜欢你的想法。我认为您不能说我的代码是实用的:str特殊情况下的代码处理的只是一种常见情况。但是,也许应该有一个新的标准属性可供代码检查,.__atomic__例如,这表明某些内容无法进一步分解。atomic()在Python中添加另一个内置函数可能为时已晚,但是也许我们可以添加from collections import atomic其他内容。
steveha 2012年

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))

而且我已经证实它可以满足您的需求。


2

我在测试用例中执行此操作。

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))

未经发电机测试,我认为如果通过发电机,您将处于下一个“收益”状态,这可能会使下游情况恶化。但是再说一次,这是一个“单元测试”


2

以“鸭子打字”的方式

try:
    lst = lst + []
except TypeError:
    #it's not a list

要么

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

分别。这避免了isinstance/ hasattr内省的东西。

您也可以反之亦然:

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

所有变体实际上都不会更改变量的内容,而是暗示了重新分配。我不确定这在某些情况下是否不受欢迎。

有趣的是,在任何情况下,如果是列表(不是元组),则在“就地”赋值时都不会引发+=no 。这就是为什么以这种方式完成分配的原因。也许有人可以阐明原因。TypeErrorlst


1

最简单的方法...使用anyisinstance

>>> console_routers = 'x'
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
False
>>>
>>> console_routers = ('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True
>>> console_routers = list('x',)
>>> any([isinstance(console_routers, list), isinstance(console_routers, tuple)])
True

1

鸭式打字的另一种形式,可以帮助区分类似字符串的对象和其他类似序列的对象。

类字符串对象的字符串表示形式是字符串本身,因此您可以检查是否从str构造函数中返回了相等的对象:

# If a string was passed, convert it to a single-element sequence
if var == str(var):
    my_list = [var]

# All other iterables
else: 
    my_list = list(var)

这应该适用于与str所有可迭代对象兼容的所有对象。


0

Python 3具有以下功能:

from typing import List

def isit(value):
    return isinstance(value, List)

isit([1, 2, 3])  # True
isit("test")  # False
isit({"Hello": "Mars"})  # False
isit((1, 2))  # False

因此,要同时检查列表和元组,将是:

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)

0
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

2
这是一种可行的方法吗?
ersh

1
欢迎来到SO。解释为什么此代码可以解答问题。
尼克,

是的,当然,我使用与此类似的方法,因为管道被视为“或”,因此您断言类型必须是列表或类型元组,输出用于处理错误的自定义消息错误。我相信它可以回答这个问题,但是我很好奇这是一种有效的方法,因为我仍在尝试编写最优化的代码。但是,我不确定此代码是否错过了可能会像列表/元组但又不是二者子类的事情,因为接受的答案如何解决了这种可能性。谢谢!
ersh

-1

做这个

if type(lst) in (list, tuple):
    # Do stuff

5
isinstance(lst,(list,tuple))
Davi Lima

@DaviLima OK,那是另一种方式。但是对于内置类型建议使用type(),对于类建议使用isinstance。
ATOzTOA

-1

在python> 3.6中

import collections
isinstance(set(),collections.abc.Container)
True
isinstance([],collections.abc.Container)
True
isinstance({},collections.abc.Container)
True
isinstance((),collections.abc.Container)
True
isinstance(str,collections.abc.Container)
False

2
在最后一次检查中,您使用type str,而不是字符串。尝试一下isinstance('my_string', collections.abc.Container),您将看到它会返回True。这是因为abc.Container提供__contains__方法,而字符串当然具有它。
乔治

-6

我倾向于这样做(如果真的必须这样做的话):

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

5
您几乎永远都不应该这样做。如果我some_var是类的子类的实例,该list()怎么办?您的代码将不知道如何处理它,即使它在“用列表执行操作”代码下也能正常工作。而且,您几乎不需要关心列表和元组之间的区别。对不起,-1。
steveha

1
无需写type(tuple())- tuple会做。列表相同。另外,strunicodeextend basestring都是真正的字符串类型,因此您需要检查一下。
好好对待您的国防部

@DrBloodmoney:意外降级。请(平凡地)编辑您的答案,以使我能够删除弃权票。
SabreWolfy 2012年

对我来说,平等似乎不是类型的有意义的比较。我测试的身份,而不是:type(i) is list。同样,type(list())list本身就是……最后,这不适用于子类。如果i实际上是OrderedDict或某种namedtuple,则此代码将视为字符串。
2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.