Ответ 1
Только в Python 2 (не Python 3):
assert not isinstance(lst, basestring)
Это на самом деле то, что вы хотите, иначе вы упустите множество вещей, которые действуют как списки, но не являются подклассами list
или tuple
.
Это то, что я обычно делаю, чтобы убедиться, что входной сигнал list
/tuple
, но не a str
. Поскольку много раз я наткнулся на ошибки, когда функция пропускает объект str
по ошибке, а целевая функция делает for x in lst
, полагая, что lst
на самом деле является list
или tuple
.
assert isinstance(lst, (list, tuple))
Мой вопрос: есть ли лучший способ достичь этого?
Только в Python 2 (не Python 3):
assert not isinstance(lst, basestring)
Это на самом деле то, что вы хотите, иначе вы упустите множество вещей, которые действуют как списки, но не являются подклассами 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)
EDIT: я изначально написал выше с проверкой на __getslice__()
, но я заметил, что в документации модуля collections
интересным методом является __getitem__()
; это имеет смысл, так как вы индексируете объект. Это кажется более фундаментальным, чем __getslice__()
, поэтому я изменил это.
H = "Hello"
if type(H) is list or type(H) is tuple:
## Do Something.
else
## Do Something.
Для 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"
Python с ароматом PHP:
def is_array(var):
return isinstance(var, (list, tuple))
Вообще говоря, тот факт, что функция, которая выполняет итерации над объектом, работает как с строками, так и с кортежами и списками, является большей особенностью, чем ошибка. Вы можете использовать isinstance
или утку, чтобы проверить аргумент, но почему вы должны?
Это звучит как риторический вопрос, но это не так. Ответ на вопрос "зачем мне проверять тип аргумента?" вероятно, собирается предложить решение реальной проблемы, а не воспринимаемую проблему. Почему это ошибка, когда строка передается функции? Кроме того: если это ошибка, когда строка передается этой функции, является ли она также ошибкой, если какой-либо другой non-list/tuple iterable передан ей? Почему, или почему нет?
Я думаю, что наиболее распространенным ответом на вопрос будет то, что разработчики, которые пишут f("abc")
, ожидают, что функция будет вести себя так, как если бы они написали f(["abc"])
. Вероятно, существуют обстоятельства, когда имеет смысл защищать разработчиков от самих себя, чем для поддержки использования итерации по символам в строке. Но я думаю, долго и упорно об этом первым.
Попробуйте это для удобства чтения и лучших практик:
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
Надеюсь, поможет.
Объект str
не имеет атрибута __iter__
>>> hasattr('', '__iter__')
False
чтобы вы могли сделать чек
assert hasattr(x, '__iter__')
и это также приведет к хорошему AssertionError
для любого другого неистребимого объекта.
Изменить: Как упоминает Тим в комментариях, это будет работать только в python 2.x, а не 3.x
Это не предназначено для прямого ответа на OP, но я хотел поделиться некоторыми связанными идеями.
Я был очень заинтересован в ответе @steveha выше, который, казалось, привел пример, где печатание утки, кажется, ломается. Однако, во-вторых, его пример подсказывает, что типизировать утику трудно, но это не означает, что str
заслуживает всякой специальной обработки.
В конце концов, тип non str
(например, определяемый пользователем тип, который поддерживает некоторые сложные рекурсивные структуры) может вызвать функцию @steveha srepr
, чтобы вызвать бесконечную рекурсию. Хотя это, по общему признанию, маловероятно, мы не можем игнорировать эту возможность. Поэтому, а не специальная обложка str
в srepr
, мы должны уточнить, что хотим srepr
делать, когда результат бесконечной рекурсии.
Может показаться, что один разумный подход состоит в том, чтобы просто разбить рекурсию в srepr
на момент list(arg) == [arg]
. Фактически, это полностью решило бы проблему с str
без isinstance
.
Однако, действительно сложная рекурсивная структура может вызвать бесконечный цикл, где list(arg) == [arg]
никогда не произойдет. Поэтому, хотя вышеуказанная проверка полезна, она недостаточна. Нам нужно что-то вроде жесткого предела на глубине рекурсии.
Я хочу сказать, что если вы планируете обрабатывать произвольные типы аргументов, обработка str
с помощью утиной печати намного проще, чем обработка более общих типов, которые вы можете (теоретически) встретить. Поэтому, если вы чувствуете необходимость исключать экземпляры str
, вы должны вместо этого потребовать, чтобы этот аргумент был экземпляром одного из немногих типов, которые вы явно указываете.
Я нахожу такую функцию с именем 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))
Непроверенный на генераторах, я думаю, что вы останетесь на следующей "доходности", если он передается в генераторе, что может привести к сбоям в работе. Но опять же, это "unittest"
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
В манере "утки", как насчет
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
является списком (не кортежем). Вот почему назначение делается таким образом. Может быть, кто-то может пролить свет на то, почему это так.
Просто сделайте это
if type(lst) in (list, tuple):
# Do stuff
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)
assert (type(lst) == list) | (type(lst) == tuple), "Not a valid lst type, cannot be string"
Если у вас уже есть pandas, вы можете просто сделать это:
variable = pd.Series(variable).tolist()
Это то, что я делаю, чтобы обеспечить список.
Я имею тенденцию делать это (если я действительно, действительно должен был):
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