Python中检查一个对象是否为列表的惯用方式是什么?

10

我有一个函数,可能接受单个数字或数字列表。最Pythonic的方法是什么?到目前为止,我想到了使用try/except块检查是否可以切片零项,即obj [0:0]。

编辑:

看起来我在下面引发了一场口水战,因为我没有提供足够的信息。为了完整起见,让我提供更多细节,这样我就可以挑选并获取最佳答案:

我正在Python 2.6上运行Django,编写一个函数,可以接收Django模型实例或查询集对象,并对其执行操作之一,其中包括使用需要列表(查询集输入)的过滤器'in',或者如果它不是列表,则我将使用'get'过滤器(django get过滤器)。


@mog:请接受一个答案,而不是让问题悬而未决。 - S.Lott
7个回答

21

在这种情况下,您通常需要检查任何可迭代对象,而不仅仅是列表——如果您接受列表或数字,拒绝(例如)元组将会很奇怪。您可能希望将一种可迭代对象视为“标量”——在Python 2.*中,这意味着strunicode。所以,有两种方式:

def isNonStringIterable(x):
  if isinstance(x, basestring):
    return False
  try: iter(x)
  except: return False
  else: return True

或者,通常更加方便的是:

def makeNonStringIterable(x):
  if isinstance(x, basestring):
    return (x,)
  try: return iter(x)
  except: return (x,)

你只需要这样写 for i in makeNonStringIterable(x): ...


14
@gs,考虑到我已经是一名Python贡献者8年了(并且已经写了很受欢迎的Python相关书籍等),我希望你能够教我,我在使用所有我帮助设计、实现和文档化的工具中哪些是错误的。当然不会是因为不使用collections.Sequence(这会没有任何好的理由排除set),比如说--对吧?-) - Alex Martelli
2
@ Alex:你说得对。我没有考虑到集合。如果你感到冒犯,我很抱歉。看起来你有最好的解决方案。(根据问题的作者真正寻找的内容而定。) - Georg Schölly
@Glenn,你认为何时需要或使用随机访问、切片等功能?如果参数可以像列表一样接受单个数字对象,那么往往函数只会进行迭代(这就是为什么我在我的解决方案中说“make”方法比同一答案中的“is”方法更好的原因)。 - Alex Martelli
3
除了try: iter(x)/except是跨平台的之外,@gs没有提到任何其他内容。在Python生产环境中,仍然经常受到2.4(目前很少见但不是没有)或2.5(App Engine等)的限制,而collections.Iterable仅适用于2.6及以上版本,而try/except方法在2.2及更高版本中同样完美运行!-) - Alex Martelli
3
他要求“检查一个对象是否为列表”。也许他实际上想检查它是否是可迭代的,但在我看来,有太少(零)关于他在知道它是什么之后实际上正在做什么的信息,以从中得出结论。(如果他提供了示例代码,表明他只是在迭代列表,那么我会同意你的观点。)我们在这种问题上之前就有过分歧,我认为我们只是在选择何时宽松解释问题方面存在差异。耸肩 - Glenn Maynard
显示剩余7条评论

13
if isinstance(your_object, list):
  print("your object is a list!")

这种方法比使用type检查更符合Python风格。

而且似乎更快:

>>> timeit('isinstance(x, list)', 'x = [1, 2, 3, 4]')
0.40161490440368652
>>> timeit('type(x) is list', 'x = [1, 2, 3, 4]')
0.46065497398376465
>>> 

我应该提到我使用了"type(x) is list",而不仅仅是"type(x)",因为两者都应该返回相同的值(True),以使比较公平。 - Vince
4
该解决方案可以防止任何不是list子类的自定义列表。 - Georg Schölly
1
@gs:你是对的。你下面的解决方案是一个很好的干净的解决方案(如果确实OP想要“listy”可迭代对象而不仅仅是列表实例,我认为这是页面上最干净的解决方案)。问题的一部分在于OP的歧义性。如果他正在测试queryset对象是否为列表,他应该只检查它是否是QuerySet的实例,然后使用len()。如果他正在测试'field__in'参数是否为列表,则我的解决方案将起作用。如果该参数更灵活,他应该选择您的解决方案。 - Vince
你或者Alex的解决方案(Alex的解决方案也很优雅,而且非常具体)。我从来不知道basestring。谢谢,Alex! - Vince

11

不需要。

这仅适用于 Python >= 2.6。如果你的目标版本在此之下,请使用Alex的解决方案

Python 支持称为鸭子类型的东西。你可以使用ABC类查找某些功能。

import collections
def mymethod(myvar):
    # collections.Sqeuence to check for list capabilities
    # collections.Iterable to check for iterator capabilities
    if not isinstance(myvar, collections.Iterable):
        raise TypeError()

有人能解释一下为什么会被踩吗?这似乎是有道理的,因为成为一个“列表”比成为“有列表特征的”要不那么有趣。 - Daren Thomas
这是这里最正确的答案,解释了为什么会有负评。(是的,我有点讽刺;这个网站上的投票系统太糟糕了。)你不应该在没有发表评论的情况下就可以给出负评。 - Glenn Maynard
@ Daren:我对我的答案进行了相当大的修改。(尽管我认为第一个版本并不是那么糟糕。) - Georg Schölly
如果我没记错的话,像collections.Sequence这样的ABCs是在2.6版本中添加的,这可能会阻止一些人使用它,但在我看来,除此之外这绝对是解决此问题的最佳方案。 - Horst Gutmann
1
它不适用于所有版本,而且Iterable是否意味着列表对象? - undefined
不是,但collections.Sequence可以,我已将其作为注释包含在代码中,因为我认为collections.Iterable可能是问题作者需要的。 - Georg Schölly

2

我希望不会打扰你,但是:你确定查询集/对象是一个好的接口吗?最好分别创建两个函数:

def fobject(i):
   # do something

def fqueryset(q):
   for obj in q:
       fobject( obj )

可能不是从列表中识别整数的“Pythonic”方式,但对我来说似乎是更好的设计。

原因是:您的函数应该处理鸭子。只要它嘎嘎叫,就打它。实际上,把鸭子拿起来,在选择正确的球杆之前将其倒置以检查腹部标记是不符合Python风格的。抱歉,请不要这样做。


非Pythonic的警告!除非OP想要根据对象执行完全不同的操作,否则它们必须是相同的函数。 - Anurag Uniyal
1
我对“不符合Python风格的警告”并不确定:如果您必须根据对象类型(列表 vs. 整数)进行区分,那么您正在根据对象执行“完全不同的操作”。 - Daren Thomas
+1:原始设计(一个函数做两件不同的事情)真的很糟糕。这显然是为对象编写的一个函数 和一个专门针对对象查询集的函数 - S.Lott
谢谢您指出这一点。我已经重新编写了代码,所以不再需要使用“queryset/object”这个东西,而是使用“filter”来进行两个操作,这样结果总是一个queryset。每天都有新的东西要学习 :-) 谢谢 - mog

1
你可以使用 isinstance 来检查变量的类型:
if isinstance(param, list):
   # it is a list
   print len(list)

此解决方案可以防止任何不是列表子类的自定义列表。 - Georg Schölly

0

我认为OP的做法,检查它是否支持他想要的,是可以的。

在这种情况下更简单的方法是不检查列表,因为根据定义它可能是许多类型之一,你可以检查输入是否为数字,如果是则对其进行操作,否则尝试将其用作列表,如果抛出异常则退出。

例如,您可能不想遍历列表,而只是想将某些内容附加到它上面(如果它是列表),否则添加到它中。

def add2(o):
    try:
        o.append(2)
    except AttributeError:
        o += 2

l=[]
n=1
s=""
add2(l)
add2(n)
add2(s) # will throw exception, let the user take care of that ;)

所以关键是答案可能因为您想用对象做什么而有所不同


你的例子不起作用是因为在Python中数字是不可变的。 - Georg Schölly
那只是一个例子,你说的“不起作用”是什么意思?谁告诉你函数add2的目的,以至于你可以推断它不起作用?也许我只是想通过加整数来消耗一些CPU。 - Anurag Uniyal

-3

只需要使用type方法吗?还是我误解了问题

if type(objectname) is list:
  do something
else:
  do something else :P

1
这是一个糟糕的解决方案。更好的方法是使用 isinstance(),因为这样可以允许子类。 - Georg Schölly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接