我通常这样做是为了确定输入是一个list/tuple—而不是一个str。因为很多时候我偶然发现错误,函数错误地传递了一个str对象,而目标函数执行for x in lst,假设lst实际上是一个list或tuple。
1
| assert isinstance(lst, (list, tuple)) |
我的问题是:有没有更好的方法来实现这个目标?
- 类型(lst)是list?
- 不是IsInstance(key,six.string_类型)
仅在python 2(而不是python 3)中:
1
| assert not isinstance(lst, basestring) |
实际上是你想要的,否则你会错过很多类似于列表的东西,但不是list或tuple的子类。
- 是的,这是正确的答案。在python 3中,basestring已不存在,您只需检查isinstance(lst, str)。
- 除了list和tuple之外,还有什么其他类型的列表类似?
- 你可以重复很多东西,比如列表,比如set,生成器表达式,迭代器。有一些异国情调的东西,比如说mmap,不那么异国情调的东西,比如说array,它们的作用与列表非常相似,可能更多的是我忘记了。
- 我对这个答案唯一的反对意见是:如果——在将来——还有另一个序列类型会传递"for x in seq"语法呢?
- 值得注意的是,这并不能保证lst是不可更改的,而原始DID(例如int将通过这张支票)
- @Petergibson-两者的组合将提供一个有效的、更严格的检查,并确保1)LST是可重写的,2)LST不是字符串。assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
- 好吧,这个解决方案只检查字符串派生的类型,但是整数、双精度或任何其他不可iterable类型呢?
- 这并不能证明这是不可能的-不知道为什么会有这么多的赞成票
- @Petergibson我相信断言可以通过使用调试来忽略"发布"。这可以为发布插入隐藏的bug。
- 有趣的是,这已经被接受并被否决了这么多,因为它实际上根本没有很好地回答这个问题。任何来到这里的人都应该通过下面的@suzanshakya查看答案,寻找更好的答案。
- 使用assert not isinstance(lst, basestring),任何不是字符串的东西都可以通过这个测试,比如dict、floats、ints。
- 断言引发异常。但这个答案根本没有提到异常处理。
记住,在python中,我们要使用"duck-typing"。所以,任何类似于列表的东西都可以被视为列表。所以,不要检查列表的类型,只要看看它是否像一个列表。
但是字符串也像一个列表,通常这不是我们想要的。有时甚至是一个问题!所以,明确检查字符串,然后使用duck输入。
这是我为好玩而写的一个函数。它是repr()的一个特殊版本,可以在尖括号("<"、">")中打印任何序列。
1 2 3 4 5 6 7
| 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()方法,它是一个字符串,所以不要认为它是一个序列;否则,如果它是可索引或不可索引的,它是一个序列:
1 2 3 4 5 6 7 8 9
| 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__()更为基本,因此我改变了上述内容。
- 我真的认为这应该是正确的答案。
- @斯坦顿,谢谢你这么说,但我认为我写这封信的时候已经有了一个可以接受的答案,我真的不希望被改变。
- @史蒂文哈:srepr是一个非常有趣的想法。但我对是否需要特例str持不同意见。是的,str是迄今为止最明显和最常见的一个不可数,它会在srepr中引起无限递归。但是我可以很容易地想象用户定义的iterables以相同的方式(有或没有好的理由)工作。我们应该承认,这种方法可能会遇到无限递归,并同意某种处理方法,而不是特殊情况str。我会把我的建议写在答案里。
- 我认为这绝对是正确的道路。然而,为了处理特殊情况(在这个场景中是字符串的情况),我认为我们最好问一个问题"一个人如何分辨差异?"例如,考虑一个函数参数,该参数可以是电子邮件地址列表或单个电子邮件地址(请记住,字符串只是字符列表)。把这个变量给一个人。怎么知道它是什么?我能想到的最简单的方法是查看列表中每个项目中有多少个字符。如果大于1,则参数肯定不能是字符列表。
- 我考虑了一下,和其他一些人讨论过,我认为srepr()还是可以的。我们需要一个递归函数来处理像嵌套在另一个列表中的列表这样的事情;但是对于字符串,我们宁愿把它们打印为"foo",而不是<'f', 'o', 'o'>。因此,在这里对字符串进行显式检查是很有意义的。此外,实际上没有其他数据类型的例子,迭代总是返回iterable,递归总是导致堆栈溢出,因此我们不需要特殊的属性来测试它("实用性胜过纯粹性")。
- @史蒂文哈:虽然is sequence是一个很好的函数,但它包括dict,但op只要求检测list/tuple参数…(检查getslice确保了此约束)
- 值得注意的是,使用getattr而不是hasattr可能更好。grokbase.com/t/python/python-3000/081pag24aq/&hellip;
- 这对xrange不起作用。我在is_sequence函数中添加了一个"not isinstance(arg,type(xrange))"。
- SREPR让我害怕超大的iTerables或发电机。
- 具有限制性不充分的__iter__属性。由于OP特别询问了列表和元组,因此X = {4:3, 2:1}没有通过Quack测试,因为list(X)丢弃了x中的一半信息。为了让x像列表一样工作,必须能够无损地将x转换为列表,反之亦然。听写不嘎嘎作响。也不做集合,因为x=[3,1,2]不等于list(set(X))。
- 这在python3中不起作用,因为字符串在python3中有一个__iter__()方法,但在python2中没有。你在is_sequence()中缺少括号,应该是:return (not hasattr(arg,"strip") and (hasattr(arg,"__getitem__") or hasattr(arg,"__iter__")))号。
1 2 3 4 5 6
| H ="Hello"
if type(H) is list or type(H) is tuple:
## Do Something.
else
## Do Something. |
- 为什么这次被否决?
- @TR33hous-可能是因为它没有添加其他答案尚未添加的任何内容。
- 如果能用的话,它是最干净的。
- 但它没有像其他评论者所指出的那样使用python的鸭式打字习惯用法(尽管它确实直接而清晰地回答了这个问题)。
- 这个答案比其他答案更不可接受,因为它不允许duck类型,而且它在子类化的简单情况下也失败了(一个典型的例子是NamedDuple类)。
- "不允许鸭子打字"并没有使答案变得不可接受,特别是考虑到这个答案实际上回答了问题。
- 我对这个答案投了反对票,但江户十一〔四〕的回答更简短、更清晰。
- 这不是想象的。它工作,实际上看起来可读性很好,但它不同于我迄今为止所阅读的所有其他代码如何解决这个常见问题。这通常应该避免。
- 不会将列表的子类视为列表。
- 可选语法:if type(H) in [list, tuple]:。
- 这个答案显然更好,因为它在处理案件时不会引发异常!
- 这个答案是错误的。如前所述,它不尊重list和tuple的子类。但任何子类型实例都是超级类型实例。以其他方式执行的代码违反了Liskov替换原则(即使违反发生在类之外)。Python开发人员完全有权期望LSP作为公共模式在任何地方都受到尊重。
对于Python 2:
1 2 3 4
| 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:
1 2 3 4
| 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") |
Changed in version 3.3: Moved Collections Abstract Base Classes to the collections.abc module. For backwards compatibility, they will continue to be visible in this module as well until version 3.8 where it will stop working.
- 真的!这真的很有效,而且比其他任何正确答案都要简洁得多。我不知道内置类型继承自collections.Sequence,但是我测试了它,我发现它们确实继承了。xrange也是如此。更好的是,这个测试排除了dict,__getitem__和__iter__都有。
- 你知道为什么inspect.getmro(list)的结果不包括Sequence吗?当getmro没有显示所有内容时,我们应该如何计算出我们可以用isinstance做什么?
- @stevejorgensen方法解析顺序定义了类搜索路径,python使用它来搜索类中要使用的正确方法。Sequence是一个抽象类。
- 在python3中,可以将isInstance(obj,baseString)替换为isInstance(obj,str),这应该是有效的。
- 在python3中,您需要而不是isinstance(obj,bytes)…如果你想要一个列表,而不仅仅是枚举字节…
具有PHP风格的python:
1 2
| def is_array(var):
return isinstance(var, (list, tuple)) |
- python是一种duck类型的语言,所以您真的应该检查var是否具有属性__getitem__。同样,名称也是误导性的,因为还有数组模块。var也可以是numpy.ndarray或任何其他类型,它有__getitem__。有关正确答案,请参阅stackoverflow.com/a/1835259/470560。
- @彼得希尔·埃多克斯1〔15〕也有埃多克斯1〔22〕,因此您的支票不排除埃多克斯1〔15〕。
- 口述检查__getitem__也是不好的建议。
一般来说,迭代对象的函数可以处理字符串、元组和列表,这一事实比bug更具特点。你当然可以用isinstance或duck输入来检查一个论点,但是为什么要这样做呢?
这听起来像是一个反问问题,但事实并非如此。"为什么我要检查论点的类型?"可能会建议解决实际问题,而不是感知到的问题。当一个字符串传递给函数时,为什么它是一个bug?另外:如果一个字符串传递给这个函数时它是一个bug,那么如果传递给它的是其他非列表/元组iterable,它也是一个bug吗?为什么?为什么?
我认为对这个问题最常见的答案可能是编写f("abc")的开发人员希望函数的行为就像他们编写f(["abc"])一样。在某些情况下,保护开发人员不受其影响可能比支持在字符串中遍历字符的用例更有意义。但我会先考虑很久再考虑。
- "但我会先考虑很久,然后再考虑。"我不会。如果函数应该是一个list-y函数,那么是的,它应该对它们进行相同的处理(例如,给出一个列表,向后吐出,类似的事情)。但是,如果函数中的一个参数可以是字符串或字符串列表(这是非常常见的需求),那么强制使用该函数的开发人员始终在数组中输入参数似乎有点过分。另外,考虑一下如何处理JSON输入。您肯定希望处理一个不同于字符串的对象列表。
请尝试此操作以获得可读性和最佳实践:
Python 2
1 2 3
| import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
# Do something |
Python 3
1 2 3
| 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'。
- 在python 3中,它是:from typing import List->isinstance([1, 2, 3], List=True,isinstance("asd", List)=False
- @朱福林,谢谢你的指点。回答更新。
str对象没有__iter__属性
1 2
| >>> hasattr('', '__iter__')
False |
所以你可以查一下
1
| assert hasattr(x, '__iter__') |
这也将为任何其他不可辩驳的对象带来一个好的AssertionError。
编辑:正如蒂姆在评论中提到的,这只在python 2.x中有效,而不是3.x。
- 小心:在python 3中,hasattr('','__iter__')返回True。当然,这是有意义的,因为您可以迭代一个字符串。
- 真的?我不知道。我一直认为这是一个很好的解决问题的方法,哦,好吧。
- 此测试在pyodbc.row上无效。它没有iter_uuu(),但它或多或少表现得像一个列表(它甚至定义了"uusetitem")。您可以迭代它的元素。len()函数可以工作,您可以为其元素编制索引。我正在努力寻找正确的组合来捕获所有列表类型,但不包括字符串。我想我会接受对"getitem"和"len"的检查,同时明确排除basestring。
这不是为了直接回答操作,但我想分享一些相关的想法。
我对上面的@steveha答案很感兴趣,它似乎给出了一个鸭子打字似乎中断的例子。然而,在第二个想法中,他的例子表明duck类型很难符合,但它并不意味着str值得任何特殊处理。
毕竟,非str类型(例如,维护一些复杂递归结构的用户定义类型)可能会导致@steveha srepr函数产生无限递归。诚然,这是不太可能的,但我们不能忽视这种可能性。因此,在srepr中,我们应该澄清当无限递归结果时,我们希望srepr做什么,而不是特殊的外壳str。
一个合理的方法似乎是简单地打破srepr中的递归,即由list(arg) == [arg]开始。实际上,这将完全解决str的问题,而不需要任何isinstance。
然而,一个非常复杂的递归结构可能会导致一个无限循环,其中list(arg) == [arg]从未发生过。因此,虽然上面的检查很有用,但还不够。我们需要像递归深度的硬限制这样的东西。
我的观点是,如果您计划处理任意参数类型,那么通过duck类型处理str要比处理可能(理论上)遇到的更一般的类型容易得多。因此,如果您觉得需要排除str实例,您应该要求该参数是您显式指定的少数类型之一的实例。
- 嗯,我喜欢你的想法。我认为您不能认为我的代码是实用的:只有一个常见的情况,即str,特殊情况代码可以处理。但是,也许应该有一个新的标准属性,代码可以检查,比如说,.__atomic__,它表示某些东西不能进一步分解。向python中添加另一个内置函数atomic()可能为时已晚,但我们可以添加from collections import atomic或其他东西。
我在TensorFlow中发现这样一个名为is_序列的函数。
1 2 3 4 5 6 7 8 9
| 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)) |
我已经证实它能满足你的需要。
我在我的测试案例中这样做。
1 2 3 4 5 6 7 8 9 10 11
| 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)) |
我认为,如果通过一台发电机,你将在下一个"收益率"中留下,这可能会把下游的事情搞砸。但同样,这是一个"单元测试"
最简单的方法…使用any和isinstance。
1 2 3 4 5 6 7 8 9 10
| >>> 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 2 3 4
| try:
lst = lst + []
except TypeError:
#it's not a list |
或
1 2 3 4
| try:
lst = lst + ()
except TypeError:
#it's not a tuple |
分别。这样就避免了isinstance和hasattr的反省。
您也可以检查反之亦然:
1 2 3 4
| try:
lst = lst + ''
except TypeError:
#it's not (base)string |
所有变量实际上并不改变变量的内容,而是意味着重新分配。我不确定在某些情况下这是否不可取。
有趣的是,如果lst是一个列表(不是一个元组),那么在任何情况下,"就地"分配+=都不会引发TypeError。这就是为什么任务是这样完成的。也许有人能解释为什么会这样。
就这样做
1 2
| if type(lst) in (list, tuple):
# Do stuff |
- isInstance(lst,(list,tuple))。
- @达维利马好吧,那是另一种方式。但对于内置类型和类的IsInstance,建议使用type()。
1
| assert (type(lst) == list) | (type(lst) == tuple),"Not a valid lst type, cannot be string" |
- 这样做可以吗?
- 欢迎光临。解释一下这段代码为什么回答这个问题会很有帮助。
- 是的,当然,我使用与此类似的方法,因为管道被视为一个或多个类型,所以您断言类型必须是一个列表或类型tuple,输出自定义消息错误以进行错误处理。我相信它回答了这个问题,但我很好奇,好像这是一种有效的方法,因为我仍然在努力掌握编写最优化代码的窍门。但是,我不确定这段代码是否遗漏了一些可能类似于列表/元组的内容,但它们都不是其中的子类,因为接受的答案是如何解决这种可能性的。谢谢!
python 3有:
1 2 3 4 5 6 7 8 9
| 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 |
因此,要同时检查列表和元组,应该是:
1 2 3 4
| from typing import List, Tuple
def isit(value):
return isinstance(value, List) or isinstance(value, Tuple) |
我倾向于这样做(如果我真的,真的必须这样做的话):
1 2 3 4 5 6 7
| 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 |
- 你几乎不应该这样做。如果i some_var是属于list()子类的类的实例,会发生什么?您的代码不知道如何处理它,即使它在"用列表做些事情"代码下也能很好地工作。你很少需要关心列表和元组之间的区别。抱歉,- 1。
- 不需要写type(tuple())——tuple就行了。相同的列表。另外,str和unicode都扩展了basestring,这是真正的字符串类型,因此您需要检查它。
- @血钱博士:意外投反对票。请编辑您的答案,使我可以删除投反对票。
- 在我看来,平等对于类型来说似乎不是一个有意义的比较。我要测试身份:type(i) is list。另外,type(list())只是list本身…最后,对于子类来说,这并不能很好地工作。如果i实际上是有序的,或者是某种命名的双重形式,则此代码将被视为字符串。