Как проверить, является ли объект списком или кортежем (но не строкой)?

Это то, что я обычно делаю, чтобы убедиться, что входной сигнал list/tuple, но не a str. Поскольку много раз я наткнулся на ошибки, когда функция пропускает объект str по ошибке, а целевая функция делает for x in lst, полагая, что lst на самом деле является list или tuple.

assert isinstance(lst, (list, tuple))

Мой вопрос: есть ли лучший способ достичь этого?

Ответ 1

Только в Python 2 (не Python 3):

assert not isinstance(lst, basestring)

Это на самом деле то, что вы хотите, иначе вы упустите множество вещей, которые действуют как списки, но не являются подклассами list или tuple.

Ответ 2

Помните, что в 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)

EDIT: я изначально написал выше с проверкой на __getslice__(), но я заметил, что в документации модуля collections интересным методом является __getitem__(); это имеет смысл, так как вы индексируете объект. Это кажется более фундаментальным, чем __getslice__(), поэтому я изменил это.

Ответ 3

H = "Hello"

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

Ответ 4

Для 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: перемещены абстрактные базовые классы коллекций в модуль collection.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"

Ответ 5

Python с ароматом PHP:

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

Ответ 6

Вообще говоря, тот факт, что функция, которая выполняет итерации над объектом, работает как с строками, так и с кортежами и списками, является большей особенностью, чем ошибка. Вы можете использовать isinstance или утку, чтобы проверить аргумент, но почему вы должны?

Это звучит как риторический вопрос, но это не так. Ответ на вопрос "зачем мне проверять тип аргумента?" вероятно, собирается предложить решение реальной проблемы, а не воспринимаемую проблему. Почему это ошибка, когда строка передается функции? Кроме того: если это ошибка, когда строка передается этой функции, является ли она также ошибкой, если какой-либо другой non-list/tuple iterable передан ей? Почему, или почему нет?

Я думаю, что наиболее распространенным ответом на вопрос будет то, что разработчики, которые пишут f("abc"), ожидают, что функция будет вести себя так, как если бы они написали f(["abc"]). Вероятно, существуют обстоятельства, когда имеет смысл защищать разработчиков от самих себя, чем для поддержки использования итерации по символам в строке. Но я думаю, долго и упорно об этом первым.

Ответ 7

Попробуйте это для удобства чтения и лучших практик:

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

Надеюсь, поможет.

Ответ 8

Объект str не имеет атрибута __iter__

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

чтобы вы могли сделать чек

assert hasattr(x, '__iter__')

и это также приведет к хорошему AssertionError для любого другого неистребимого объекта.

Изменить: Как упоминает Тим ​​в комментариях, это будет работать только в python 2.x, а не 3.x

Ответ 9

Это не предназначено для прямого ответа на OP, но я хотел поделиться некоторыми связанными идеями.

Я был очень заинтересован в ответе @steveha выше, который, казалось, привел пример, где печатание утки, кажется, ломается. Однако, во-вторых, его пример подсказывает, что типизировать утику трудно, но это не означает, что str заслуживает всякой специальной обработки.

В конце концов, тип non str (например, определяемый пользователем тип, который поддерживает некоторые сложные рекурсивные структуры) может вызвать функцию @steveha srepr, чтобы вызвать бесконечную рекурсию. Хотя это, по общему признанию, маловероятно, мы не можем игнорировать эту возможность. Поэтому, а не специальная обложка str в srepr, мы должны уточнить, что хотим srepr делать, когда результат бесконечной рекурсии.

Может показаться, что один разумный подход состоит в том, чтобы просто разбить рекурсию в srepr на момент list(arg) == [arg]. Фактически, это полностью решило бы проблему с str без isinstance.

Однако, действительно сложная рекурсивная структура может вызвать бесконечный цикл, где list(arg) == [arg] никогда не произойдет. Поэтому, хотя вышеуказанная проверка полезна, она недостаточна. Нам нужно что-то вроде жесткого предела на глубине рекурсии.

Я хочу сказать, что если вы планируете обрабатывать произвольные типы аргументов, обработка str с помощью утиной печати намного проще, чем обработка более общих типов, которые вы можете (теоретически) встретить. Поэтому, если вы чувствуете необходимость исключать экземпляры str, вы должны вместо этого потребовать, чтобы этот аргумент был экземпляром одного из немногих типов, которые вы явно указываете.

Ответ 10

Я нахожу такую ​​функцию с именем 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))

И я проверил, что он отвечает вашим потребностям.

Ответ 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))

Непроверенный на генераторах, я думаю, что вы останетесь на следующей "доходности", если он передается в генераторе, что может привести к сбоям в работе. Но опять же, это "unittest"

Ответ 12

isinstance простой способ... используя 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

Ответ 13

В манере "утки", как насчет

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

или же

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

соответственно. Это позволяет избежать интроспекции isinstance/hasattr.

Вы также можете проверить наоборот:

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

Все варианты фактически не изменяют содержимое переменной, но подразумевают переназначение. Я не уверен, может ли это быть нежелательным при некоторых обстоятельствах.

Интересно, что с присваиванием "на месте" += нет TypeError в любом случае, если lst является списком (не кортежем). Вот почему назначение делается таким образом. Может быть, кто-то может пролить свет на то, почему это так.

Ответ 14

Просто сделайте это

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

Ответ 15

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)

Ответ 16

assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"

Ответ 17

Если у вас уже есть pandas, вы можете просто сделать это:

variable = pd.Series(variable).tolist()

Это то, что я делаю, чтобы обеспечить список.

Ответ 18

Я имею тенденцию делать это (если я действительно, действительно должен был):

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