如何检查一个对象是列表还是元组(但不是字符串)?
这是我通常所做的,以确定输入是 list
/tuple
- 但不是 str
。因为很多时候我偶然发现了一些错误,其中函数错误地传递了 str
对象,并且目标函数执行 for x in lst
假设 lst
实际上是一个列表
或元组
。
assert isinstance(lst, (list, tuple))
我的问题是:有更好的方法来实现这一目标吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
以“鸭子打字”的方式,
或者
分别怎么样。这避免了
isinstance
/hasattr
内省的事情。您也可以反之亦然进行检查:
所有变体实际上并不更改变量的内容,而是意味着重新分配。我不确定在某些情况下这是否是不可取的。
有趣的是,使用“就地”赋值
+=
,如果lst
是一个列表,在任何情况下都不会引发TypeError
em> (不是元组)。这就是为什么任务是这样完成的。也许有人可以解释为什么会这样。In "duck typing" manner, how about
or
respectively. This avoids the
isinstance
/hasattr
introspection stuff.You could also check vice versa:
All variants do not actually change the content of the variable, but imply a reassignment. I'm unsure whether this might be undesirable under some circumstances.
Interestingly, with the "in place" assignment
+=
noTypeError
would be raised in any case iflst
is a list (not a tuple). That's why the assignment is done this way. Maybe someone can shed light on why that is.鸭子类型的另一个版本,有助于区分字符串类对象和其他序列类对象。
类字符串对象的字符串表示形式是字符串本身,因此您可以检查是否从
str
构造函数返回一个相等的对象:这应该适用于与
str
兼容的所有对象code> 以及各种可迭代对象。Another version of duck-typing to help distinguish string-like objects from other sequence-like objects.
The string representation of string-like objects is the string itself, so you can check if you get an equal object back from the
str
constructor:This should work for all objects compatible with
str
and for all kinds of iterable objects.最简单的方法...使用
any
和isinstance
simplest way... using
any
andisinstance
Python 3 有这样的:
所以要检查列表和元组,它会是:
Python 3 has this:
So to check for both Lists and Tuples, it would be:
它也很容易扩展以进行更多检查,例如 numpy 数组(
ndarray
),而无需导入 numpy。It is also easy to expand for more check, e.g. numpy array (
ndarray
) without importing numpy.只要这样做
Just do this
在Python中>3.6
in python >3.6
我倾向于这样做(如果我真的、真的不得不这么做的话):
I tend to do this (if I really, really had to):
仅在 python 2 中(不是 python 3):
实际上是您想要的,否则您会错过很多像列表一样的东西,但不是
list
或的子类元组
。In python 2 only (not python 3):
Is actually what you want, otherwise you'll miss out on a lot of things which act like lists, but aren't subclasses of
list
ortuple
.请记住,在 Python 中我们要使用“鸭子类型”。因此,任何像列表一样的东西都可以被视为列表。因此,不要检查列表的类型,只需查看它是否像列表一样工作。
但字符串也像列表一样,而这通常不是我们想要的。有时甚至是一个问题!因此,显式检查字符串,然后使用鸭子类型。
这是我为了好玩而编写的一个函数。它是
repr()
的特殊版本,可以打印尖括号('<'、'>')中的任何序列。总体而言,这是干净而优雅的。但是
isinstance()
检查在那里做什么呢?这就是一种黑客行为。但这是必要的。该函数在任何类似于列表的东西上递归地调用自身。如果我们不对字符串进行特殊处理,那么它将被视为一个列表,并一次分割一个字符。但随后递归调用会尝试将每个字符视为一个列表——并且它会起作用!即使是单字符字符串也可以用作列表!该函数将继续递归调用自身,直到堆栈溢出。
像这样的函数,依赖于分解要完成的工作的每个递归调用,必须对字符串进行特殊处理 - 因为您无法分解低于单字符字符串级别的字符串,甚至无法分解单字符字符串- 字符串的作用类似于列表。
注意:
try
/except
是表达我们意图的最简洁的方式。但是,如果此代码在某种程度上对时间要求严格,我们可能希望用某种测试来替换它,以查看arg
是否是一个序列。我们或许应该测试行为,而不是测试类型。如果它有.strip()
方法,那么它是一个字符串,所以不要将其视为序列;否则,如果它是可索引或可迭代的,则它是一个序列:编辑:我最初编写上面时检查了
__getslice__()
但我注意到在collections
模块文档中,有趣的方法是__getitem__()
;这是有道理的,这就是索引对象的方式。这似乎比 __getslice__() 更基本,所以我改变了上面的内容。Remember that in Python we want to use "duck typing". So, anything that acts like a list can be treated as a list. So, don't check for the type of a list, just see if it acts like a list.
But strings act like a list too, and often that is not what we want. There are times when it is even a problem! So, check explicitly for a string, but then use duck typing.
Here is a function I wrote for fun. It is a special version of
repr()
that prints any sequence in angle brackets ('<', '>').This is clean and elegant, overall. But what's that
isinstance()
check doing there? That's kind of a hack. But it is essential.This function calls itself recursively on anything that acts like a list. If we didn't handle the string specially, then it would be treated like a list, and split up one character at a time. But then the recursive call would try to treat each character as a list -- and it would work! Even a one-character string works as a list! The function would keep on calling itself recursively until stack overflow.
Functions like this one, that depend on each recursive call breaking down the work to be done, have to special-case strings--because you can't break down a string below the level of a one-character string, and even a one-character string acts like a list.
Note: the
try
/except
is the cleanest way to express our intentions. But if this code were somehow time-critical, we might want to replace it with some sort of test to see ifarg
is a sequence. Rather than testing the type, we should probably test behaviors. If it has a.strip()
method, it's a string, so don't consider it a sequence; otherwise, if it is indexable or iterable, it's a sequence:EDIT: I originally wrote the above with a check for
__getslice__()
but I noticed that in thecollections
module documentation, the interesting method is__getitem__()
; this makes sense, that's how you index an object. That seems more fundamental than__getslice__()
so I changed the above.Python 3:
Python 2:
Python 3:
Python 2:
具有 PHP 风格的 Python:
Python with PHP flavor:
试试这个以获得可读性和最佳实践:
Python2 - isinstance()
Python3 - isinstance()
希望有帮助。
Try this for readability and best practices:
Python2 - isinstance()
Python3 - isinstance()
Hope it helps.
一般来说,迭代对象的函数适用于字符串以及元组和列表,这一事实更多的是功能而不是错误。您当然可以使用 isinstance 或鸭子类型来检查参数,但为什么要这样做呢?
这听起来像是一个反问句,但事实并非如此。 “为什么我应该检查参数的类型?”的答案可能会建议解决实际问题,而不是感知到的问题。为什么将字符串传递给函数时会出现错误?另外:如果将字符串传递给此函数时是一个错误,那么如果将其他非列表/元组可迭代传递给它,这也是一个错误吗?为什么,或者为什么不呢?
我认为这个问题最常见的答案可能是编写
f("abc")
的开发人员期望该函数的行为就像他们编写的f([" abc"])
。在某些情况下,保护开发人员免受自身侵害可能比支持迭代字符串中的字符的用例更有意义。但我首先会认真思考。Generally speaking, the fact that a function which iterates over an object works on strings as well as tuples and lists is more feature than bug. You certainly can use
isinstance
or duck typing to check an argument, but why should you?That sounds like a rhetorical question, but it isn't. The answer to "why should I check the argument's type?" is probably going to suggest a solution to the real problem, not the perceived problem. Why is it a bug when a string is passed to the function? Also: if it's a bug when a string is passed to this function, is it also a bug if some other non-list/tuple iterable is passed to it? Why, or why not?
I think that the most common answer to the question is likely to be that developers who write
f("abc")
are expecting the function to behave as though they'd writtenf(["abc"])
. There are probably circumstances where it makes more sense to protect developers from themselves than it does to support the use case of iterating across the characters in a string. But I'd think long and hard about it first.我并不是想直接回答OP,而是想分享一些相关的想法。
我对上面@steveha 的回答非常感兴趣,它似乎给出了一个鸭子打字似乎被破坏的例子。然而,再想一想,他的例子表明鸭子类型很难遵守,但它并不表明
str
值得任何特殊处理。毕竟,非
str
类型(例如,维护一些复杂递归结构的用户定义类型)可能会导致@stevehasrepr
函数导致无限递归。虽然这确实不太可能发生,但我们不能忽视这种可能性。因此,我们应该明确当无限递归结果时我们希望srepr
做什么,而不是在srepr
中使用特殊大小写的str
。似乎一种合理的方法是在
list(arg) == [arg]
时简单地中断srepr
中的递归。事实上,这将完全解决str
的问题,而不需要任何isinstance
。然而,一个非常复杂的递归结构可能会导致无限循环,其中
list(arg) == [arg]
永远不会发生。因此,虽然上述检查很有用,但还不够。我们需要对递归深度进行硬性限制。我的观点是,如果您打算处理任意参数类型,通过鸭子类型处理
str
比处理您可能(理论上)遇到的更通用的类型要容易得多。因此,如果您觉得需要排除str
实例,您应该要求参数是您明确指定的少数类型之一的实例。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 @stevehasrepr
function to cause an infinite recursion. While this is admittedly rather unlikely, we can't ignore this possibility. Therefore, rather than special-casingstr
insrepr
, we should clarify what we wantsrepr
to do when an infinite recursion results.It may seem that one reasonable approach is to simply break the recursion in
srepr
the momentlist(arg) == [arg]
. This would, in fact, completely solve the problem withstr
, without anyisinstance
.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 excludestr
instances, you should instead demand that the argument is an instance of one of the few types that you explicitly specify.我在tensorflow中找到了一个名为 is_sequence。
我已经验证它满足您的需求。
I find such a function named is_sequence in tensorflow.
And I have verified that it meets your needs.
str
对象没有__iter__
属性,因此您可以进行检查
,这也会为任何其他非对象引发一个很好的
AssertionError
也是可迭代对象。编辑:
正如 Tim 在评论中提到的,这仅适用于 python 2.x,不适用于 3.x
The
str
object doesn't have an__iter__
attributeso you can do a check
and this will also raise a nice
AssertionError
for any other non-iterable object too.Edit:
As Tim mentions in the comments, this will only work in python 2.x, not 3.x
我在我的测试用例中这样做。
未经在发电机上进行测试,我认为如果通过发电机,您将处于下一个“产量”,这可能会将下游的事情搞砸。但话又说回来,这是一个“单元测试”
I do this in my testcases.
Untested on generators, I think you are left at the next 'yield' if passed in a generator, which may screw things up downstream. But then again, this is a 'unittest'