这就是我通常做,以确定输入是一个list
/ tuple
-但不是str
。因为很多时候我偶然发现了一个错误,即一个函数str
错误地传递了一个对象,而目标函数确实for x in lst
假定这lst
实际上是一个list
or tuple
。
assert isinstance(lst, (list, tuple))
我的问题是:是否有更好的方法来实现这一目标?
这就是我通常做,以确定输入是一个list
/ tuple
-但不是str
。因为很多时候我偶然发现了一个错误,即一个函数str
错误地传递了一个对象,而目标函数确实for x in lst
假定这lst
实际上是一个list
or tuple
。
assert isinstance(lst, (list, tuple))
我的问题是:是否有更好的方法来实现这一目标?
Answers:
仅在python 2中(不是python 3):
assert not isinstance(lst, basestring)
实际上就是您想要的,否则您会错过很多像列表一样的东西,但它们不是list
or的子类tuple
。
basestring
它消失了,您只需检查即可isinstance(lst, str)
。
set
生成器表达式,迭代器。有类似的异国情调的东西mmap
,少有的异国情调的东西array
就像列表一样,也许我忘了更多。
lst
迭代是可迭代的,而原始方法却可以保证迭代(例如,一个int会通过这张支票)
assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
请记住,在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__()
因此我更改了上面的内容。
srepr
是一个非常有趣的想法。但是对于是否需要特殊情况,我和您持有不同的意见str
。是的,str
是迄今为止最明显,最常见的迭代方法,它将导致中的无限递归srepr
。但是我可以轻松想象用户定义的可迭代对象的行为方式相同(有或没有充分的理由)。除了特殊情况外str
,我们应该承认这种方法可能会遇到无限递归,并同意采取某种处理方式。我将我的建议发布在答案中。
srepr()
都很好。我们需要一个递归函数来处理诸如嵌套在另一个列表中的列表之类的事情。但是对于字符串,我们宁愿将它们打印"foo"
为<'f', 'o', 'o'>
。因此,在此处显式检查字符串很有意义。另外,实际上,没有任何其他数据类型的示例,其中迭代总是返回可迭代的,而递归总是会导致堆栈溢出,因此我们不需要特殊的属性来对此进行测试(“实用性胜过纯度”)。
__iter__()
方法,但在Python 2中却没有。您在中缺少括号is_sequence()
,它应显示为:return (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
H = "Hello"
if type(H) is list or type(H) is tuple:
## Do Something.
else
## Do Something.
if isinstance( H, (list, tuple) ): ...
更简短。
if type(H) in [list, tuple]:
对于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"
collections.Sequence
但是我对其进行了测试,发现它们确实可以继承。也是xrange
。更好的是,该测试排除了dict
同时具有__getitem__
和的测试__iter__
。
inspect.getmro(list)
不包括在内Sequence
吗?isinstance
当getmro
无法显示所有内容时,我们应该如何弄清楚该怎么办?
Sequence
是一个抽象类。
具有PHP风格的Python:
def is_array(var):
return isinstance(var, (list, tuple))
__getitem__
。这个名称也令人误解,因为还有阵列模块。var也可以是numpy.ndarray或任何其他类型,具有__getitem__
。有关正确的答案,请参见stackoverflow.com/a/1835259/470560。
str
也有__getitem__
您的支票不排除str
__getitem__
在这里检查是不好的建议。
一般来说,在对象上进行迭代的函数不仅可以处理错误,还可以处理字符串,元组和列表。您当然可以使用isinstance
或鸭式输入来检查参数,但是为什么要这么做呢?
这听起来像是个反问,但事实并非如此。答案为“为什么我应该检查参数的类型?” 可能会建议解决实际问题,而不是感知到的问题。将字符串传递给函数时,为什么会出错?另外:如果将字符串传递给此函数是一个错误,是否将其他非列表/元组可迭代传递给它也是一个错误吗?为什么或者为什么不?
我认为这个问题的最常见答案可能是 f("abc")
期望该函数的行为就像编写的一样f(["abc"])
。在某些情况下,保护开发人员免受自身侵害比支持对字符串中的字符进行迭代的用例更有意义。但是我首先会考虑很长时间。
尝试此操作以提高可读性和最佳做法:
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
希望能帮助到你。
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
-> isinstance([1, 2, 3], List
= True
和isinstance("asd", List)
= False
该str
对象没有__iter__
属性
>>> hasattr('', '__iter__')
False
所以你可以检查一下
assert hasattr(x, '__iter__')
这也AssertionError
将为其他任何不可迭代的对象带来好处。
编辑: 正如蒂姆在评论中提到的那样,这仅适用于python 2.x,而不是3.x
hasattr('','__iter__')
return True
。当然这是有意义的,因为您可以遍历字符串。
这并不是要直接回答OP,而是要分享一些相关想法。
我对上面的@steveha回答非常感兴趣,这似乎举了一个鸭子输入似乎中断的示例。换个角度说,他的例子表明鸭子的分类很难遵循,但是并不能说明str
值得任何特殊处理。
毕竟,非str
类型(例如,维护一些复杂的递归结构的用户定义类型)可能导致@steveha srepr
函数引起无限递归。尽管这确实不太可能,但我们不能忽略这种可能性。因此,与其特殊外壳str
中srepr
,我们应该明确,我们想要什么srepr
在无限递归产生时的事情情况。
似乎一种合理的方法是srepr
暂时中断当前递归list(arg) == [arg]
。这,其实,彻底解决这个问题str
,没有任何isinstance
。
但是,真正复杂的递归结构可能会导致无限循环,list(arg) == [arg]
永远不会发生。因此,尽管上面的检查很有用,但还不够。我们需要对递归深度进行严格限制。
我的观点是,如果您打算处理任意参数类型,则str
通过鸭子类型进行处理要比处理(理论上)遇到的更通用类型容易得多。因此,如果您需要排除str
实例,则应该要求该参数是您明确指定的几种类型之一的实例。
str
特殊情况下的代码处理的只是一种常见情况。但是,也许应该有一个新的标准属性可供代码检查,.__atomic__
例如,这表明某些内容无法进一步分解。atomic()
在Python中添加另一个内置函数可能为时已晚,但是也许我们可以添加from collections import atomic
其他内容。
我在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))
而且我已经证实它可以满足您的需求。
我在测试用例中执行此操作。
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))
未经发电机测试,我认为如果通过发电机,您将处于下一个“收益”状态,这可能会使下游情况恶化。但是再说一次,这是一个“单元测试”
以“鸭子打字”的方式
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 。这就是为什么以这种方式完成分配的原因。也许有人可以阐明原因。TypeError
lst
最简单的方法...使用any
和isinstance
>>> 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
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)
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
在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
str
,而不是字符串。尝试一下isinstance('my_string', collections.abc.Container)
,您将看到它会返回True
。这是因为abc.Container
提供__contains__
方法,而字符串当然具有它。
我倾向于这样做(如果真的必须这样做的话):
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
some_var
是类的子类的实例,该list()
怎么办?您的代码将不知道如何处理它,即使它在“用列表执行操作”代码下也能正常工作。而且,您几乎不需要关心列表和元组之间的区别。对不起,-1。
type(tuple())
- tuple
会做。列表相同。另外,str
和unicode
extend basestring
都是真正的字符串类型,因此您需要检查一下。
type(i) is list
。同样,type(list())
它list
本身就是……最后,这不适用于子类。如果i
实际上是OrderedDict或某种namedtuple,则此代码将视为字符串。