如何检查一个对象是列表还是元组(但不是字符串)?

发布于 2024-08-13 16:20:29 字数 317 浏览 7 评论 0 原文

这是我通常所做的,以确定输入是 list/tuple - 但不是 str。因为很多时候我偶然发现了一些错误,其中函数错误地传递了 str 对象,并且目标函数执行 for x in lst 假设 lst实际上是一个列表元组

assert isinstance(lst, (list, tuple))

我的问题是:有更好的方法来实现这一目标吗?

This is what I normally do in order to ascertain that the input is a list/tuple - but not a str. Because many times I stumbled upon bugs where a function passes a str object by mistake, and the target function does for x in lst assuming that lst is actually a list or tuple.

assert isinstance(lst, (list, tuple))

My question is: is there a better way of achieving this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

妄想挽回 2024-08-20 16:20:30

以“鸭子打字”的方式,

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

所有变体实际上并不更改变量的内容,而是意味着重新分配。我不确定在某些情况下这是否是不可取的。

有趣的是,使用“就地”赋值+=,如果lst是一个列表,在任何情况下都不会引发TypeError em> (不是元组)。这就是为什么任务是这样完成的。也许有人可以解释为什么会这样。

In "duck typing" manner, how about

try:
    lst = lst + []
except TypeError:
    #it's not a list

or

try:
    lst = lst + ()
except TypeError:
    #it's not a tuple

respectively. This avoids the isinstance / hasattr introspection stuff.

You could also check vice versa:

try:
    lst = lst + ''
except TypeError:
    #it's not (base)string

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 += no TypeError would be raised in any case if lst is a list (not a tuple). That's why the assignment is done this way. Maybe someone can shed light on why that is.

女皇必胜 2024-08-20 16:20:30

鸭子类型的另一个版本,有助于区分字符串类对象和其他序列类对象。

类字符串对象的字符串表示形式是字符串本身,因此您可以检查是否从 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 兼容的所有对象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:

# 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)

This should work for all objects compatible with str and for all kinds of iterable objects.

始终不够爱げ你 2024-08-20 16:20:30

最简单的方法...使用 anyisinstance

>>> 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

simplest way... using any and 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
淡淡绿茶香 2024-08-20 16:20:30

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)

Python 3 has this:

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

So to check for both Lists and Tuples, it would be:

from typing import List, Tuple

def isit(value):
    return isinstance(value, List) or isinstance(value, Tuple)
瑾兮 2024-08-20 16:20:30
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
奈何桥上唱咆哮 2024-08-20 16:20:30
assert type(lst).__name__ in ('tuple', 'list')

它也很容易扩展以进行更多检查,例如 numpy 数组(ndarray),而无需导入 numpy。

assert type(lst).__name__ in ('tuple', 'list')

It is also easy to expand for more check, e.g. numpy array (ndarray) without importing numpy.

冬天的雪花 2024-08-20 16:20:30

只要这样做

if type(lst) in (list, tuple):
    # Do stuff

Just do this

if type(lst) in (list, tuple):
    # Do stuff
深居我梦 2024-08-20 16:20:30

在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

in 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
我家小可爱 2024-08-20 16:20:30

我倾向于这样做(如果我真的、真的不得不这么做的话):

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 tend to do this (if I really, really had to):

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
独守阴晴ぅ圆缺 2024-08-20 16:20:29

仅在 python 2 中(不是 python 3):

assert not isinstance(lst, basestring)

实际上是您想要的,否则您会错过很多像列表一样的东西,但不是 list 的子类元组

In python 2 only (not python 3):

assert not isinstance(lst, basestring)

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 or tuple.

怀中猫帐中妖 2024-08-20 16:20:29

请记住,在 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__() 更基本,所以我改变了上面的内容。

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 ('<', '>').

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

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 if arg 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:

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)

EDIT: I originally wrote the above with a check for __getslice__() but I noticed that in the collections 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.

绝影如岚 2024-08-20 16:20:29
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.
H = "Hello"

if type(H) is list or type(H) is tuple:
    ## Do Something.
else
    ## Do Something.
酒与心事 2024-08-20 16:20:29

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 or a dictionary.")

版本 3.3 中的更改:将“Collections Abstract Base Classes”的全局命名空间从 abc 移至 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 or dictionary."

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 or a dictionary.")

Changed in version 3.3: Moved global namespace of "Collections Abstract Base Classes" from abc to 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.

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 or dictionary."
薄荷港 2024-08-20 16:20:29

具有 PHP 风格的 Python:

def is_array(var):
    return isinstance(var, (list, tuple))

Python with PHP flavor:

def is_array(var):
    return isinstance(var, (list, tuple))
耳根太软 2024-08-20 16:20:29

试试这个以获得可读性和最佳实践:

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

希望有帮助。

Try this for readability and best practices:

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

Hope it helps.

逆光飞翔i 2024-08-20 16:20:29

一般来说,迭代对象的函数适用于字符串以及元组和列表,这一事实更多的是功能而不是错误。您当然可以使用 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 written f(["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.

和我恋爱吧 2024-08-20 16:20:29

我并不是想直接回答OP,而是想分享一些相关的想法。

我对上面@steveha 的回答非常感兴趣,它似乎给出了一个鸭子打字似乎被破坏的例子。然而,再想一想,他的例子表明鸭子类型很难遵守,但它并不表明 str 值得任何特殊处理。

毕竟,非str类型(例如,维护一些复杂递归结构的用户定义类型)可能会导致@steveha srepr函数导致无限递归。虽然这确实不太可能发生,但我们不能忽视这种可能性。因此,我们应该明确当无限递归结果时我们希望 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 @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.

转身以后 2024-08-20 16:20:29

我在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))

我已经验证它满足您的需求。

I find such a function named is_sequence in tensorflow.

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))

And I have verified that it meets your needs.

旧伤还要旧人安 2024-08-20 16:20:29

str 对象没有 __iter__ 属性

>>> hasattr('', '__iter__')
False 

,因此您可以进行检查

assert hasattr(x, '__iter__')

,这也会为任何其他非对象引发一个很好的 AssertionError也是可迭代对象。

编辑:
正如 Tim 在评论中提到的,这仅适用于 python 2.x,不适用于 3.x

The str object doesn't have an __iter__ attribute

>>> hasattr('', '__iter__')
False 

so you can do a check

assert hasattr(x, '__iter__')

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

不回头走下去 2024-08-20 16:20:29

我在我的测试用例中这样做。

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))

未经在发电机上进行测试,我认为如果通过发电机,您将处于下一个“产量”,这可能会将下游的事情搞砸。但话又说回来,这是一个“单元测试”

I do this in my testcases.

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))

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'

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文