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

时间:2009-12-02 18:53:08

标签: python list types assert

这是我通常所做的,以确定输入是list / tuple - 而不是str。因为很多次我偶然发现函数错误地传递str对象的错误,而目标函数假设for x in lst实际上是lst或{{1} }}

list

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

19 个答案:

答案 0 :(得分:322)

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

assert not isinstance(lst, basestring)

实际上是你想要的,否则你会错过很多像列表一样的东西,但不是listtuple的子类。

答案 1 :(得分:168)

请记住,在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
总的来说,这是干净而优雅的。但isinstance()检查那里做了什么?那是一种黑客攻击。但这很重要。

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

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

注意: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 :(得分:118)

H = "Hello"

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

答案 3 :(得分:61)

对于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"

对于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 or unicode")
  

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

答案 4 :(得分:31)

Python的PHP风格:

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

答案 5 :(得分:10)

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

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

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

答案 6 :(得分:7)

请尝试以获取可读性和最佳做法:

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

希望它有所帮助。

答案 7 :(得分:6)

str对象没有__iter__属性

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

所以你可以做检查

assert hasattr(x, '__iter__')

这也会为任何其他不可迭代的对象引发一个很好的AssertionError

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

答案 8 :(得分:5)

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

我对上面的@steveha回答非常感兴趣,这似乎给出了鸭子打字似乎打破的例子。然而,第二个想法是,他的例子表明鸭子打字很难符合,但表明str值得进行任何特殊处理。

毕竟,非str类型(例如,维护一些复杂递归结构的用户定义类型)可能会导致@steveha srepr函数导致无限递归。虽然这是不太可能的,但我们不能忽视这种可能性。因此,我们应该澄清在无限递归结果时我们希望str做什么,而不是srepr中的特殊套srepr

似乎一种合理的方法是在srepr list(arg) == [arg]时刻打破递归。事实上,这将完全解决str的问题,而没有任何isinstance

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

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

答案 9 :(得分:3)

我找到了一个名为is_sequence in tensorflow的函数。

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

我已经确认它符合您的需求。

答案 10 :(得分: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))

在发电机上未经测试,我认为如果传入发电机,你会被留在下一个'收益',这可能会使下游的事情变得棘手。但话说回来,这是一个'单元测试'

答案 11 :(得分: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

答案 12 :(得分:1)

以“鸭子打字”的方式,

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

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

有趣的是,如果+= list (不是,则在任何情况下使用“就地”分配TypeError都不会引发lst >元组)。这就是为什么以这种方式完成分配的原因。也许有人可以阐明原因。

答案 13 :(得分: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兼容的所有对象以及所有可迭代的对象。

答案 14 :(得分:0)

就这样做

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

答案 15 :(得分: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)

答案 16 :(得分:0)

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

答案 17 :(得分:-3)

在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

答案 18 :(得分:-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