这是我通常所做的,以确定输入是list
/ tuple
- 但不是str
. 因为很多时候我偶然发现了函数错误传递str
对象的错误,而目标函数确实for x in lst
假设它lst
实际上是 a list
or tuple
。
assert isinstance(lst, (list, tuple))
我的问题是:有没有更好的方法来实现这一目标?
仅在 python 2 中(不是 python 3):
assert not isinstance(lst, basestring)
实际上是你想要的,否则你会错过很多像列表一样的东西,但不是 or 的子list
类tuple
。
请记住,在 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()
支票在那里做什么?这是一种黑客行为。但这是必不可少的。
这个函数递归地调用任何像列表一样的东西。如果我们没有特别处理这个字符串,那么它就会被当作一个列表来处理,并且一次拆分一个字符。但随后递归调用会尝试将每个字符视为一个列表——它会起作用!即使是一个字符的字符串也可以作为一个列表!该函数将继续递归调用自身,直到堆栈溢出。
像这样的函数,依赖于分解要完成的工作的每个递归调用,必须是特殊情况的字符串——因为你不能分解低于一个字符的字符串,甚至是一个字符串- 字符串就像一个列表。
注意: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__()
我改变了上面的更基本。
H = "Hello"
if type(H) is list or type(H) is tuple:
## Do Something.
else
## Do Something.
蟒蛇 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 a dictionary.")
在 3.3 版更改: 将“集合抽象基类”的全局命名空间从
abc
模块collections.abc
移动。为了向后兼容,它们将继续在此模块中可见,直到版本 3.8 停止工作。
蟒蛇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 or dictionary."
带有 PHP 风格的 Python:
def is_array(var):
return isinstance(var, (list, tuple))
一般来说,迭代对象的函数适用于字符串以及元组和列表这一事实比错误更具功能。你当然可以使用isinstance
或回避打字来检查一个论点,但你为什么要这样做呢?
这听起来像是一个修辞问题,但事实并非如此。“我为什么要检查参数的类型?”的答案 可能会建议解决实际问题,而不是感知问题。为什么将字符串传递给函数时会出现错误?另外:如果将字符串传递给此函数时出现错误,那么如果将其他一些非列表/元组迭代传递给它,这也是错误吗?为什么或者为什么不?
我认为这个问题最常见的答案可能是编写的开发人员f("abc")
期望函数的行为就像他们编写的一样f(["abc"])
。在某些情况下,保护开发人员免受他们自己的伤害可能比支持迭代字符串中的字符的用例更有意义。但我会先考虑很久。
试试这个以获得可读性和最佳实践:
Python2 - isinstance()
import types
if isinstance(lst, types.ListType) or isinstance(lst, types.TupleType):
# Do something
Python3 - isinstance()
import typing
if isinstance(lst, typing.List) or isinstance(lst, typing.Tuple):
# Do something
希望能帮助到你。
This is not intended to directly answer the OP, but I wanted to share some related ideas.
I was very interested in @steveha answer above, which seemed to give an example where duck typing seems to break. On second thought, however, his example suggests that duck typing is hard to conform to, but it does not suggest that str
deserves any special handling.
After all, a non-str
type (e.g., a user-defined type that maintains some complicated recursive structures) may cause @steveha srepr
function to cause an infinite recursion. While this is admittedly rather unlikely, we can't ignore this possibility. Therefore, rather than special-casing str
in srepr
, we should clarify what we want srepr
to do when an infinite recursion results.
It may seem that one reasonable approach is to simply break the recursion in srepr
the moment list(arg) == [arg]
. This would, in fact, completely solve the problem with str
, without any isinstance
.
However, a really complicated recursive structure may cause an infinite loop where list(arg) == [arg]
never happens. Therefore, while the above check is useful, it's not sufficient. We need something like a hard limit on the recursion depth.
My point is that if you plan to handle arbitrary argument types, handling str
via duck typing is far, far easier than handling the more general types you may (theoretically) encounter. So if you feel the need to exclude str
instances, you should instead demand that the argument is an instance of one of the few types that you explicitly specify.
该str
对象没有__iter__
属性
>>> hasattr('', '__iter__')
False
所以你可以做个检查
assert hasattr(x, '__iter__')
这也AssertionError
将为任何其他不可迭代的对象带来好处。
编辑: 正如蒂姆在评论中提到的,这只适用于 python 2.x,而不是 3.x
我在 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
另一个版本的鸭子类型有助于区分类似字符串的对象和其他类似序列的对象。
类字符串对象的字符串表示是字符串本身,因此您可以检查是否从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
这应该适用于与所有类型的可迭代对象兼容的所有对象。
最简单的方法...使用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
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
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)
就这样做
if type(lst) in (list, tuple):
# Do stuff
在蟒蛇> 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
我倾向于这样做(如果我真的,真的必须这样做):
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