Это то, что я обычно делаю, чтобы убедиться, что вход является list
/ tuple
-, но не а str
. Потому что много раз я сталкивался с ошибками, когда функция str
по ошибке пропускает объект, а целевая функция for x in lst
предполагает, что lst
это на самом деле list
или tuple
.
assert isinstance(lst, (list, tuple))
Мой вопрос: есть ли лучший способ добиться этого?
Ответы:
Только в Python 2 (не Python 3):
Это на самом деле то, что вы хотите, иначе вы упустите много вещей, которые действуют как списки, но не являются подклассами
list
илиtuple
.источник
basestring
уже нет, и вы просто проверьтеisinstance(lst, str)
.set
, выражения генератора, итераторы. Есть экзотические вещи, такие какmmap
менее экзотические,array
которые похожи на списки, и, возможно, многое другое я забыл.lst
повторяемость, в то время как оригинал (например, int прошел бы эту проверку)assert isinstance(lst, (list, tuple)) and assert not isinstance(lst, basestring)
Помните, что в Python мы хотим использовать «Duck Typing». Таким образом, все, что действует как список, может рассматриваться как список. Поэтому не проверяйте тип списка, просто посмотрите, действует ли он как список.
Но строки тоже действуют как список, и часто это не то, что мы хотим. Есть моменты, когда это даже проблема! Так что, проверяйте явно строку, но затем используйте утку.
Вот функция, которую я написал для удовольствия. Это специальная версия,
repr()
которая печатает любую последовательность в угловых скобках ('<', '>').Это чисто и элегантно, в целом. Но что там
isinstance()
делает эта проверка? Это что-то вроде хака. Но это важно.Эта функция вызывает себя рекурсивно для всего, что действует как список. Если бы мы не обрабатывали строку специально, то она была бы обработана как список и разделена по одному символу за раз. Но тогда рекурсивный вызов попытается обработать каждый символ как список - и это сработает! Даже односимвольная строка работает как список! Функция будет продолжать вызывать себя рекурсивно, пока стек не переполнится.
Подобные функции, которые зависят от каждого рекурсивного вызова, разбивающего выполняемую работу, должны иметь строки специального случая - потому что вы не можете разбить строку ниже уровня односимвольной строки, и даже одну Строка -character действует как список.
Примечание:
try
/except
является самым чистым способом выразить наши намерения. Но если бы этот код был как-то критичным по времени, мы могли бы заменить его каким-то тестом, чтобы увидеть,arg
является ли последовательность. Вместо того, чтобы тестировать тип, мы, вероятно, должны проверить поведение. Если у него есть.strip()
метод, это строка, поэтому не считайте ее последовательностью; в противном случае, если он индексируется или повторяется, это последовательность:РЕДАКТИРОВАТЬ: Первоначально я написал выше с проверкой для,
__getslice__()
но я заметил, что вcollections
документации модуля, интересный метод является__getitem__()
; это имеет смысл, вот как вы индексируете объект. Это кажется более фундаментальным, чем__getslice__()
я изменил выше.источник
srepr
очень интересная идея. Но я придерживаюсь мнения, отличного от вас, о том, нужно ли это в особом случаеstr
. Да,str
безусловно, самая очевидная и распространенная итерация, которая может вызвать бесконечную рекурсию вsrepr
. Но я легко могу представить пользовательские итерации, которые ведут себя одинаково (с уважительной причиной или без нее). Вместо особого случаяstr
мы должны признать, что этот подход может столкнуться с бесконечной рекурсией, и согласиться на какой-то способ борьбы с ним. Я отправлю свое предложение в ответ.srepr()
все в порядке. Нам нужна рекурсивная функция для обработки таких вещей, как список, вложенный в другой список; но для строк мы предпочли бы, чтобы они печатались как, а"foo"
не как<'f', 'o', 'o'>
. Так что явная проверка строки имеет смысл. Кроме того, на самом деле нет других примеров типов данных, в которых итерация всегда возвращает итерацию, а рекурсия всегда вызывает переполнение стека, поэтому нам не нужно специальное свойство для проверки этого («Практичность превосходит чистоту»).__iter__()
метод в Python 3, но не в Python 2. Вы пропускаете круглые скобкиis_sequence()
, оно должноreturn (not hasattr(arg, "strip") and (hasattr(arg, "__getitem__") or hasattr(arg, "__iter__")))
источник
if isinstance( H, (list, tuple) ): ...
он короче и понятнее.if type(H) in [list, tuple]:
Для Python 3:
Для Python 2:
источник
collections.Sequence
но я проверил это, и я вижу, что они делают. Так и делаетxrange
. Еще лучше, этот тест исключаетdict
, который имеет__getitem__
и__iter__
.inspect.getmro(list)
не включает в себя,Sequence
хотя? Как мы должны выяснить, что мы можем сделать,isinstance
когдаgetmro
не показывает все?Sequence
это абстрактный класс.Python со вкусом PHP:
источник
__getitem__
. Также название вводит в заблуждение, так как есть также модуль массива. И переменная также может быть numpy.ndarray или любой другой тип, который имеет__getitem__
. См. Stackoverflow.com/a/1835259/470560 для правильного ответа.str
также имеет__getitem__
поэтому ваш чек не исключаетstr
__getitem__
- плохой совет.Вообще говоря, тот факт, что функция, которая выполняет итерации по объекту, работает со строками, а также с кортежами и списками, является скорее особенностью, чем ошибкой. Вы, конечно, можете использовать
isinstance
или не печатать, чтобы проверить аргумент, но зачем вам это?Это звучит как риторический вопрос, но это не так. Ответ на вопрос «почему я должен проверять тип аргумента?» вероятно, собирается предложить решение реальной проблемы, а не предполагаемой проблемы. Почему это ошибка, когда в функцию передается строка? Кроме того: если это ошибка, когда строка передается этой функции, это также ошибка, если ей передается какая-либо иная итерация, не относящаяся к списку / кортежу? Почему или почему нет?
Я думаю, что наиболее распространенным ответом на этот вопрос, вероятно, будет то, что разработчики, которые пишут
f("abc")
, ожидают, что функция будет вести себя так, как будто они написалиf(["abc"])
. Вероятно, существуют обстоятельства, когда имеет смысл защитить разработчиков от самих себя, а не поддерживать вариант использования итерации по символам в строке. Но я бы сначала долго об этом думал.источник
Попробуйте это для удобства чтения и лучших практик:
python2
python3
Надеюсь, поможет.
источник
AttributeError: module 'types' has no attribute 'ListType'
from typing import List
->isinstance([1, 2, 3], List
=True
иisinstance("asd", List)
=False
У
str
объекта нет__iter__
атрибутатак что вы можете сделать проверку
и это также поднимет хороший
AssertionError
для любого другого не повторяемого объекта тоже.Изменить: как Тим упоминает в комментариях, это будет работать только в Python 2.x, а не 3.x
источник
hasattr('','__iter__')
возвращаетсяTrue
. И, конечно, это имеет смысл, поскольку вы можете перебирать строки.Это не предназначено для прямого ответа на ФП, но я хотел поделиться некоторыми связанными идеями.
Я был очень заинтересован в ответе @steveha выше, который, кажется, дал пример, где печать утки, кажется, ломается. Однако, после второго размышления, его пример предполагает, что с типизацией утки трудно соответствовать, но он не предполагает, что она
str
заслуживает особой обработки.В конце концов, не
str
тип (например, пользовательский тип, который поддерживает некоторые сложные рекурсивные структуры) может привести к тому, чтоsrepr
функция @steveha вызовет бесконечную рекурсию. Хотя это по общему признанию довольно маловероятно, мы не можем игнорировать эту возможность. Поэтому, а не специальный кожухstr
вsrepr
, мы должны уточнить , что мы хотимsrepr
сделать , когда бесконечное результатов рекурсии.Может показаться, что один разумный подход - просто прервать рекурсию в
srepr
данный моментlist(arg) == [arg]
. Это, на самом деле, полностью решит проблемуstr
, без каких-либоisinstance
.Тем не менее, действительно сложная рекурсивная структура может вызвать бесконечный цикл, где это
list(arg) == [arg]
никогда не происходит. Поэтому, хотя вышеуказанная проверка полезна, ее недостаточно. Нам нужно что-то вроде жесткого ограничения на глубину рекурсии.Моя точка зрения заключается в том, что если вы планируете обрабатывать произвольные типы аргументов, обработка с
str
помощью утиной типизации намного, намного проще, чем обработка более общих типов, с которыми вы можете (теоретически) столкнуться. Поэтому, если вы чувствуете необходимость исключитьstr
экземпляры, вам следует вместо этого потребовать, чтобы аргумент был экземпляром одного из немногих типов, которые вы явно указали.источник
str
, который обрабатывает код специального случая. Но, может быть, должно появиться новое стандартное свойство, которое код может проверять,.__atomic__
скажем, которое сигнализирует о том, что что-то нельзя сломать дальше. Возможно, уже слишком поздно, чтобы добавить еще одну встроенную функциюatomic()
в Python, но, возможно, мы можем добавитьfrom collections import atomic
или что-то еще.Я нахожу такую функцию с именем is_sequence в тензорном потоке .
И я убедился, что он отвечает вашим потребностям.
источник
Я делаю это в моих тестовых случаях.
Не проверенный на генераторах, я думаю, что вы останетесь на следующем уровне «доходности», если передадите его в генератор, который может привести к ухудшению работы. Но опять же, это «unittest»
источник
В манере "утки", как насчет
или
соответственно. Это позволяет избежать
isinstance
/hasattr
самоанализ вещи.Вы также можете проверить наоборот:
Все варианты фактически не изменяют содержимое переменной, но подразумевают переназначение. Я не уверен, может ли это быть нежелательным при некоторых обстоятельствах.
Интересно, что с назначением «на месте» в любом случае
+=
неTypeError
будет поднято значение, еслиlst
это список (не кортеж ). Вот почему назначение сделано таким образом. Может быть, кто-то может пролить свет на то, почему это так.источник
самый простой способ ... используя
any
иisinstance
источник
Другая версия утиной типизации, помогающая отличать строковые объекты от других похожих на последовательность объектов.
Строковое представление строковых объектов - это сама строка, поэтому вы можете проверить, получаете ли вы равный объект из
str
конструктора:Это должно работать для всех объектов, совместимых с
str
и для всех видов итерируемых объектов.источник
Python 3 имеет это:
Таким образом, чтобы проверить списки и кортежи, было бы:
источник
источник
Просто сделай это
источник
в питоне> 3.6
источник
str
, а не строку. Попробуйте,isinstance('my_string', collections.abc.Container)
и вы увидите, что он вернетсяTrue
. Это потому, что онabc.Container
предоставляет__contains__
метод, и, конечно, у строк.Я склонен делать это (если я действительно, действительно должен был):
источник
some_var
являюсь экземпляром класса, который является подклассомlist()
? Ваш код не будет иметь никакого представления о том, что с ним делать, даже если он будет отлично работать в коде «сделать что-то со списком». И вам редко нужно заботиться о разнице между списком и кортежем. Извините, -1.type(tuple())
-tuple
сделаем. То же самое для списка. Кроме того, обаstr
иunicode
extensionbasestring
, который является настоящим типом строки, так что вы хотите проверить это вместо этого.type(i) is list
. Кроме того,type(list())
это простоlist
само по себе ... Наконец, это не работает изящно с подклассами. Еслиi
это на самом деле и OrderedDict, или какой-то именованный кортеж, этот код будет обрабатываться как строка.