在Python中,测试变量是否包含列表或元组的最佳方法是什么?(即收集)
我是不是像这里建议的那样邪恶?http://www.canonical.org/~kragen/isinstance/
更新:我想区分列表和字符串的最常见原因是,当我有一些无限深的嵌套树/数据结构,如字符串列表列表列表等,我正在用递归算法进行探索,我需要知道什么时候碰到了"叶"节点。
- 广泛地认为类型检查是邪恶的有点轻而易举。它是语言的一部分。如果它是如此邪恶,应该有人写一篇文章来消除它。
- 如果您得到了其他类型的可ITerable集合(例如集合或字符串),那么您希望拥有什么作为您的行为呢?
- @亚当·克罗斯兰:"这是语言的一部分。"就像被零除一样。这是可以避免的。在这种情况下,如果没有额外的信息,这可能是完全不必要的。python中的大多数类型检查都是不必要的。因为并非所有的都是不必要的,所以需要进行一些类型检查。但这并不意味着它是有用的、有价值的,甚至是一个好主意。
- 所以你是说需要进行某种类型检查,但尽管如此,这是一个无用的、毫无价值的和坏主意。对不起,这是不合理的。
- "XXX是邪恶的"这个词的构思很糟糕,误导性的简写是"你要求做的事情表明你不明白什么时候应该使用它,而且你几乎肯定想要别的东西"。这里最有可能发生这种情况。
- 我并没有广泛地将其视为邪恶。我写了一篇短文,写的是什么时候邪恶,什么时候合理。那篇文章可能有很多东西——对、错、清楚、含糊、有趣、无聊——但有一点不是对这一技巧的广泛否定。
- 请注意,由于PEP 3119的存在已经变得更加正确。由于抽象的基类,isInstance现在是决定类是否支持常规收集协议等内容的最正确方法。
- 此外,通常情况下,对于延迟方法调用这样的事情,最好快速失败;很难调试延迟方法调用稍后失败的原因。在python 3中没有"callable(x)";检查给定对象是否可以在不调用它的情况下调用的正确方法是执行isInstance(obj,collections.abc.callable)
- 正如我在回答的一条评论中所解释的,有时有必要确定是否订购了某个容器,而这可能只能通过类型检查实现(或者在某些情况下,可能是getattr(obj, '__getitem__'),但这很难看)。当容器对象中元素的顺序很重要时,不检查这一点的算法可能会在不同的运行中生成不同的结果!我不相信类型检查是邪恶的,因为它可能是必要的。
1 2 3 4 5 6
| if type(x) is list:
print 'a list'
elif type(x) is tuple:
print 'a tuple'
else:
print 'neither a tuple or a list' |
- +1肯定是最易读的答案。
- 似乎不起作用:type([])==>list;type([])is list===>false
- 在python 2.7.5中:type([]) is list返回True。
- type(x) in [list,tuple]较短。
- 如果x和类型(x)是列表:以避免[]不匹配
- 我不得不向下滚动这么多。D
如果需要,可以使用isinstance。它有点邪恶,因为它排除了自定义序列、迭代器和您可能实际需要的其他东西。但是,有时如果某人(例如)传递字符串,您需要采取不同的行为。我倾向于明确检查str或unicode,如下:
1 2
| import types
isinstance(var, types.StringTypes) |
注意,不要把types.StringType误认为types.StringTypes。后者合并了str和unicode对象。
许多人认为,types模块已经过时,而只需直接检查对象的类型,因此,如果您不想使用上述模块,您也可以对str和unicode进行明确检查,如下所示:
1
| isinstance(var, (str, unicode)): |
编辑:
更好的是:
1
| isinstance(var, basestring) |
结束编辑
在这两种情况下,您都可以像获得正常序列一样返回到行为,让非序列引发适当的异常。
请看,类型检查的"邪恶"之处并不是您可能希望对某个特定类型的对象采取不同的行为,而是您人为地限制了您的函数,使其无法使用意外的对象类型来执行正确的操作,否则将执行正确的操作。如果最终回退未选中类型,则删除此限制。应该注意的是,过多的类型检查是一种代码味道,它表明您可能想要进行一些重构,但这并不一定意味着您应该从getgo中避免进行重构。
- 类型模块是一个有点历史性的工件。如docs.python.org/dev/library/types.html模块类型中所述,如果您真的必须检查str类型,那么您应该直接使用它,而不是使用types.StringType,后者只是它的别名。但我不认为这个答案回答了被问到的问题,因为那是关于"收藏"。除非您使用的是一个新的python,足以让abc模块不适合您使用isinstance来检查,即使这样,我还是建议尽可能避免检查。
- assert isinstance(u'abc', str) == False。我同意直接检查类型比使用types模块更好,但是types.StringTypes做了str不做的事情:它为str和unicode对象返回true。我将编辑我的回复,以提供一个重复检查作为备选方案。
- 我意识到我没有直接回答检查收藏品的问题,但实际的问题是"江户十一〔五〕恶吗?"我举了一个反例,(1)使用isinstance是非邪恶的,因为有一个回退意味着它不会破坏ducktyping,(2)是一个很好的解决方案,因为人们有一个非常普遍的动机,想要检查是否是list或tuple的东西(即从字符串中消除它们的歧义)。
- 我同意这一点,但需要注意的是,自定义类型的行为通常也像字符串一样有用。但Python的OO只走了这么远…
- 你想做什么?
- 唉,这在python 3.2上不起作用,因为types模块根本不包含stringtype或stringtype。
- @Anttihaapala这就是我提供另一个版本的原因,在python 3中,这个版本将转换为isinstance(foo, (str, bytes))。
- 不管怎样,你的建议很好。然而,当我创建需要接受单个对象或复合集合的方法,并将前者视为1的集合时,总是需要分别检查我的"集合"是否恰好是str、unicode或bytes。python的一个不好的特性-"字符串是不可变的字符集合"并不像bdfl最初想象的那样有用;)
使用isinstance没有任何问题,只要它不是多余的。如果一个变量应该只是一个列表/元组,那么记录这个接口,并这样使用它。否则支票是完全合理的:
1 2 3 4
| if isinstance(a, collections.Iterable):
# use as a container
else:
# not a container! |
这种类型的检查确实有一些很好的用例,例如标准的字符串startswith/endswith方法(尽管为了准确起见,这些方法是在cpython的C中实现的,使用显式检查来查看它是否是元组——有多种方法可以解决这个问题,如您所链接到的文章中所提到的)。
显式检查通常比尝试将对象用作容器并处理异常要好——这可能导致部分或不必要地运行代码时出现各种问题。
- 这是一种检查变量是否可重设的好方法。但是,对于这个问题,它可能不起作用。请注意,字符串也是不可重复的,可能会产生假阳性。
- 一个set对象也是一个不可迭代的对象,这意味着尽管你可以从中弹出元素,但它不能保证一定的顺序,这对于某些算法来说是非常危险的。在元素排序很重要的情况下,使用此代码段的算法可能会在不同的运行中生成不同的结果!
将参数记录为需要成为序列,并将其用作序列。不要检查类型。
在python 2.8上,type(list) is list返回false。我建议用这种可怕的方式来比较这一类型:
1 2
| if type(a) == type([]) :
print"variable a is a list" |
(至少在我的系统上,在Mac OS X Yosemite上使用水蟒)
- 类型(a)is list是否也计算为false?
- 你是说python 2.7.8吗?python.org/dev/peps/pep-0404/官方声明
- 你好,我很好奇:你为什么认为你的例子"可怕"?
- type(list) is list返回False,因为type(list)是type而不是list。type(list()) is list或列表的任何其他实例将返回True。
怎么样:hasattr(a,"__iter__")?
它指示是否可以将返回的对象作为生成器进行迭代。默认情况下,元组和列表可以,但字符串类型不能。
- 我觉得这个很有用。
- 对于字符串(至少在python3上)也会得到true。
- 这是错误的答案。因为类型"str"也具有方法"iter"。@西贝斯塔姆是对的。类型"set"也可以是iterable,但不可排序。
- 口述也有__iter__。
python使用"duck-typing",也就是说,如果变量kwak与duck类似,那么它一定是duck。在您的情况下,您可能希望它是可iterable的,或者希望在某个索引处访问该项。你应该这样做:即使用for var:或var[idx]中的对象在try块中,如果你得到一个例外,它不是一只鸭子……
- 问题在于,如果var是字符串迭代,则可能会出现意外结果。
- 尽管布莱恩·M·亨特(BrianM.Hunt)说了这一事实,但就请求宽恕而非许可而言,他的解决方案相当于一个Python式的解决方案。
1 2 3
| >>> l = []
>>> l.__class__.__name__ in ('list', 'tuple')
True |
如果您只需要知道是否可以对变量使用foo[123]表示法,那么可以检查hasattr(foo, '__getitem__')是否存在__getitem__属性(这是python在通过索引访问时调用的属性)。
如果您真的想把任何东西作为函数参数来处理,那么必须进行更复杂的测试。
1
| type(a) != type('') and hasattr(a,"__iter__") |
不过,通常只需说明一个函数期望它是可执行的,然后只检查type(a) != type('')。
另外,对于一个字符串,你可能有一个简单的处理路径,或者你会很好地进行拆分等,所以你不想对字符串大喊大叫,如果有人给你发送了一些奇怪的东西,就让他有一个例外。
原则上,我同意上面的ignacio,但是您也可以使用type来检查某个东西是元组还是列表。
1 2 3 4 5 6
| >>> a = (1,)
>>> type(a)
(type 'tuple')
>>> a = [1]
>>> type(a)
(type 'list') |
另一种简单的方法是确定变量是列表变量还是元组变量,或者通常检查变量类型:
1 2 3 4 5
| def islist(obj):
if ("list" in str(type(obj)) ): return True
else : return False |