Я хочу написать функцию, которая принимает параметр, который может быть последовательностью или одним значением. Тип значения - str, int и т. д., Но я, не, хочу, чтобы оно было ограничено жестко заданным списком. Другими словами, я хочу знать, является ли параметр X последовательностью или чем-то, что мне нужно преобразовать в последовательность, чтобы в дальнейшем избежать использования специального регистра. я мог бы сделать
type(X) in (list, tuple)
но могут быть другие типы последовательностей, о которых я не знаю, и нет общего базового класса.
-N.
Редактировать: См. Мой «ответ» ниже, чтобы узнать, почему большинство из этих ответов мне не помогают. Может, тебе есть что предложить.
@pi: правильно, и это настоящая проблема. Все "хорошие" ответы ниже не учитывают это.
Хм. На самом деле я бы сказал, что вы на самом деле не определили проблему. Если бы вы изначально сказали, в чем заключалась задача, вы могли бы сразу получить более полезные ответы.
Суетливость заключается в том, что OP означает не «последовательность» в смысле Python (где последовательность имеет четко определенное значение), а в более свободном смысле «несколько вещей» против «одной вещи».






Я думаю, что я бы сделал, это проверил, есть ли у объекта определенные методы, указывающие, что это последовательность. Я не уверен, есть ли официальное определение того, что составляет последовательность. Лучшее, о чем я могу думать, это то, что он должен поддерживать нарезку. Итак, вы могли бы сказать:
is_sequence = '__getslice__' in dir(X)
Вы также можете проверить, какие функции вы собираетесь использовать.
Как указал пи в комментарии, одна проблема заключается в том, что строка является последовательностью, но вы, вероятно, не хотите рассматривать ее как единицу. Вы можете добавить явную проверку того, что тип не является str.
hasattr (X, 'Getlice') - более эффективный способ сделать то же самое
Вы можете передать свой параметр во встроенную функцию len () и проверить, вызывает ли это ошибку. Как говорили другие, строковый тип требует особой обработки.
Согласно документации, функция len может принимать последовательность (строку, список, кортеж) или словарь.
Вы можете проверить, является ли объект строкой со следующим кодом:
x.__class__ == "".__class__
Лучший способ проверить строку - isinstance(x, basestr).
Исправленный ответ:
Я не знаю, совпадает ли ваше представление о «последовательности» с тем, что в руководствах Python называется «Тип последовательности», но в этом случае вам следует поискать метод __Contains__. Это метод, который Python использует для проверки «если что-то в объекте:»
if hasattr(X, '__contains__'):
print "X is a sequence"
Мой первоначальный ответ:
Я бы проверил, реализует ли полученный вами объект интерфейс итератора:
if hasattr(X, '__iter__'):
print "X is a sequence"
Для меня это самое близкое соответствие вашему определению последовательности, поскольку это позволит вам сделать что-то вроде:
for each in X:
print each
Файловые объекты имеют интерфейс итератора. Могут быть и другие объекты, которые не являются истинными последовательностями.
@Dave: вы правы насчет файловых объектов, имеющих интерфейс итератора. Я добавлю новый исправленный ответ
Я повторю комментарий, сделанный к другому сообщению - вы должны использовать hasattr (X, 'содержит') вместо 'содержит' в dir (X)
@Moe: разумно ожидать, что dir () никогда не вернет очень длинный список, поэтому разница в производительности между in и hasattr будет незначительной. И я думаю, что «in» яснее и лучше показывает намерение проверки, хотя, конечно, это субъективно.
@ Рикардо: Я согласен с Мо. hasattr и понятнее, и быстрее.
Оба на самом деле здесь ошибочны (поскольку hasattr (list, 'содержит') тоже верно), но IMHO hasattr тоже лучше. dir () удобен для интерактивного использования, а не предназначен для получения исчерпывающих списков методов. См. docs.python.org/library/functions.html#dir
(из связанного выше документа): поскольку dir () предоставляется в первую очередь для удобства использования в интерактивном приглашении, он пытается предоставить интересный набор имен больше, чем он пытается предоставить строго или последовательно определенный набор имен, и его подробное поведение может меняться в зависимости от выпуска.
@ Брайан: ты прав. Я никогда не знал об ограничениях dir и всегда считал выбор между dir и hasattr делом своего выбора. Я соответствующим образом отредактирую свой ответ. Спасибо за информацию.
Одно небольшое исправление: это hasattr (X, имя метода), а не hasattr, являющийся методом объекта.
В подобных случаях я предпочитаю всегда использовать тип последовательности или всегда использовать скаляр. Строки не будут единственными типами, которые плохо себя ведут в этой настройке; скорее, любой тип, который имеет совокупное использование и допускает итерацию по своим частям, может вести себя неправильно.
Последовательности описаны здесь: https://docs.python.org/2/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer-xrange
Таким образом, последовательности - это не то же самое, что итерируемые объекты. Я думаю, что последовательность должна быть реализована
__getitem__, тогда как итерируемые объекты должны реализовывать __iter__.
Так, например, строка - это последовательности и не реализует __iter__, объекты xrange - это последовательности и не реализуют __getslice__.
Но, судя по тому, что вы хотели сделать, я не уверен, что вам нужны последовательности, а скорее итерируемые объекты.
Так что выбирайте hasattr("__getitem__", X), если вам нужны последовательности, но лучше выбирайте hasattr("__iter__", X), если вам, например, не нужны струны.
hasattr недостаточно для проверки его итерации. Список тип также будет иметь член iter, но сам не повторяемый. т.е. hasattr (list, 'iter') == True, но iter (list) -> «TypeError: объект 'type' не повторяется». Он также будет пропускать объекты, реализующие getitem, но не iter
Хорошо для конкретного случая списка: это встроенный метод, а iter - статический метод (1 аргумент). В общем, да, утиный набор не работает! Но iter () создает итерируемый объект из не повторяемого (а не решение). Единственное «верное» решение - исключить / включить желаемые типы, как это сделали вы и noamtm.
Самый простой способ - проверить, можно ли превратить его в итератор. т.е.
try:
it = iter(X)
# Iterable
except TypeError:
# Not iterable
Однако, если вам нужно убедиться, что это перезапускаемая последовательность или последовательность с произвольным доступом (т.е. не генератор и т. д.), Этого подхода будет недостаточно.
Как отмечали другие, строки также являются итеративными, поэтому, если вам нужно так исключить их (особенно важно при рекурсии по элементам, поскольку list (iter ('a')) снова дает ['a'], вам может потребоваться специально исключить их с:
if not isinstance(X, basestring)
Мне это не помогает, потому что объект str повторяется, но я хочу рассматривать его как единый элемент.
Вот почему я упомянул проверку экземпляра. Поскольку строки являются полностью повторяемыми и индексируемыми, невозможно исключить их на основе их «последовательности», которая также не исключает какой-либо другой тип последовательности. Единственный реальный вариант - добавить явную проверку isinstance, чтобы рассматривать их как исключение.
Вы задаете неправильный вопрос. Вы не пытаетесь определять типы в Python; вы обнаруживаете поведение.
def _use_single_val(v):
print v + 1 # this will fail if v is not a value type
def _use_sequence(s):
print s[0] # this will fail if s is not indexable
def use_seq_or_val(item):
try:
_use_single_val(item)
except TypeError:
pass
try:
_use_sequence(item)
except TypeError:
pass
raise TypeError, "item not a single value or sequence"
Обновлено: исправлено, чтобы обрабатывать «последовательность или одно значение», о котором говорится в вопросе.
Я не знаю, почему это было отклонено - я думаю, что это лучший намек. Рабочий процесс с динамически типизированным языком должен быть не «какого типа этот объект», а «что этот объект может делать?» Это то же самое, что и тестирование браузеров, для какого они браузера, а не того, как они ведут себя в контексте.
Разве не об этом спрашивают? У него есть метод, основанный на обнаружении типа (тип (X) в (список, кортеж)), но ему нужен метод, который будет обнаруживать на основе поведения (действует как последовательность). Для его целей тоже нет «неправильных» параметров, он просто хочет, чтобы они делали разные вещи.
-1, я согласен с определением поведения, но вопрос в перегрузке на основе последовательности / непоследовательности
Начиная с версии 2.6, используйте абстрактные базовые классы.
>>> import collections
>>> isinstance([], collections.Sequence)
True
>>> isinstance(0, collections.Sequence)
False
Кроме того, ABC можно настроить для учета исключений, например, не рассматривать строки как последовательности. Вот пример:
import abc
import collections
class Atomic(object):
__metaclass__ = abc.ABCMeta
@classmethod
def __subclasshook__(cls, other):
return not issubclass(other, collections.Sequence) or NotImplemented
Atomic.register(basestring)
После регистрации класс Атомный можно использовать с это экземпляр и isubclass:
assert isinstance("hello", Atomic) == True
Это все же намного лучше, чем жестко запрограммированный список, потому что вам нужно только зарегистрировать исключения из правила, а внешние пользователи кода могут зарегистрировать свои собственные.
Обратите внимание, что в Python 3 изменился синтаксис для определения метаклассов и удален абстрактный суперкласс basestring, что требует использования чего-то вроде следующего:
class Atomic(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, other):
return not issubclass(other, collections.Sequence) or NotImplemented
Atomic.register(str)
При желании можно написать код, совместимый как с Python 2.6+ и 3.x, но для этого требуется использование немного более сложной техники, которая динамически создает необходимый абстрактный базовый класс, тем самым избегая синтаксических ошибок из-за разницы в синтаксисе метаклассов. . По сути, это то же самое, что и функция with_metaclass() модуля Бенджамина Петерсона шесть.
class _AtomicBase(object):
@classmethod
def __subclasshook__(cls, other):
return not issubclass(other, collections.Sequence) or NotImplemented
class Atomic(abc.ABCMeta("NewMeta", (_AtomicBase,), {})):
pass
try:
unicode = unicode
except NameError: # 'unicode' is undefined, assume Python >= 3
Atomic.register(str) # str includes unicode in Py3, make both Atomic
Atomic.register(bytes) # bytes will also be considered Atomic (optional)
else:
# basestring is the abstract superclass of both str and unicode types
Atomic.register(basestring) # make both types of strings Atomic
В версиях до 2.6 в модуле operator есть средства проверки типов.
>>> import operator
>>> operator.isSequenceType([])
True
>>> operator.isSequenceType(0)
False
>>> assert operator.isSequenceType ("hello") == True (как указано в другом месте, строки являются последовательностями, и Coady, я уверен, хорошо знает .... исходный вопрос был недостаточно задан.
Я здесь новенький, поэтому не знаю, как это сделать правильно. Хочу ответить на свои ответы:
Проблема со всеми вышеупомянутыми способами заключается в том, что str считается последовательностью (она повторяема, имеет __getitem__ и т. д.), Но обычно обрабатывается как один элемент.
Например, функция может принимать аргумент, который может быть либо именем файла, либо списком имен файлов. Какой самый питонический способ для функции определять первое из второго?
Должен ли я опубликовать это как новый вопрос? Отредактировать исходный?
I Ответ Коуди является лучшим, потому что он позволяет выбирать, какие классы считаются последовательностями или нет, независимо от того, какие специальные методы они могут иметь или не иметь.
IMHO, способ python - передать список как * list. Как в:
myfunc(item)
myfunc(*items)
Фактически myfunc(*items) передает кортеж списка элементов.
Если проблема связана со строками, определите последовательность и отфильтруйте специальный случай строк:
def is_iterable(x):
if type(x) == str:
return False
try:
iter(x)
return True
except TypeError:
return False
The problem with all of the above mentioned ways is that str is considered a sequence (it's iterable, has getitem, etc.) yet it's usually treated as a single item.
For example, a function may accept an argument that can either be a filename or a list of filenames. What's the most Pythonic way for the function to detect the first from the latter?
Судя по пересмотренному вопросу, похоже, что вы хотите что-то вроде:
def to_sequence(arg):
'''
determine whether an arg should be treated as a "unit" or a "sequence"
if it's a unit, return a 1-tuple with the arg
'''
def _multiple(x):
return hasattr(x,"__iter__")
if _multiple(arg):
return arg
else:
return (arg,)
>>> to_sequence("a string")
('a string',)
>>> to_sequence( (1,2,3) )
(1, 2, 3)
>>> to_sequence( xrange(5) )
xrange(5)
Не гарантируется, что это будет обрабатывать типы все, но он достаточно хорошо справляется с упомянутыми вами случаями и должен делать правильные вещи для большинства встроенных типов.
При его использовании убедитесь, что все, что получает выходные данные, может обрабатывать итерации.
Как отметил @Jim Brissom в комментарии о этот ответ к связанному с ним вопросу, строки, не имеющие атрибута __iter__, были несоответствием, которое было исправлено в Python 3, что делает этот подход несовместимым с будущим.
Обратите внимание, что объект типа str также является типом последовательности!