Hasattr (obj, '__iter__') против коллекций

Я видел пару сообщений, рекомендующих isinstance(obj, collections.Sequence) вместо hasattr(obj, '__iter__'), чтобы определить, что-то есть список.

len (объект) или hasattr (объект, __iter__)?

Python: проверьте, является ли объект последовательностью

Сначала я был взволнован, потому что тестирование, если объект __iter__ всегда казался мне грязным. Но после дальнейшего обзора это все же кажется лучшим решением, потому что ни один из тестов isinstance на collection не дает одинаковых результатов. collections.Sequence близок, но возвращает строки True для строк.

hasattr(obj, '__iter__')
    set([]): True
    {}: True
    []: True
    'str': False
    1: False

isinstance(obj, collections.Iterable)
    set([]): True
    {}: True
    []: True
    'str': True
    1: False

isinstance(obj, collections.Iterator)
    set([]): False
    {}: False
    []: False
    'str': False
    1: False

isinstance(obj, collections.Sequence)
    set([]): False
    {}: False
    []: True
    'str': True
    1: False

Вот код, который я использовал для создания этого:

import collections

testObjs = [
    set(),
    dict(),
    list(),
    'str',
    1
]

print "hasattr(obj, '__iter__')"
for obj in testObjs:
    print '    %r: %r' % (obj, hasattr(obj, '__iter__'))
print

print "isinstance(obj, collections.Iterable)"
for obj in testObjs:
    print '    %r: %r' % (obj, isinstance(obj, collections.Iterable))
print

print "isinstance(obj, collections.Iterator)"
for obj in testObjs:
    print '    %r: %r' % (obj, isinstance(obj, collections.Iterator))
print

print "isinstance(obj, collections.Sequence)"
for obj in testObjs:
    print '    %r: %r' % (obj, isinstance(obj, collections.Sequence))
print

Я что-то упустил или hasattr(obj, '__iter__') все еще лучший вариант для тестирования, если что-то итерабельно?

EDIT: Меня интересуют только встроенные типы: dict, list и set. ( EDIT: это глупо:) )

РЕДАКТИРОВАТЬ: Я должен был включить прецедент, который заставил меня заглядывать в это. У меня есть функция, которая принимает аргумент, который может быть единственным значением или последовательностью. Поэтому я хочу определить, что это такое, и превратить его в последовательность, если это единственное значение, чтобы я мог обработать его как последовательность после этого.

if hasattr(arg, '__iter__'):
    arg= set(arg)
else:
    arg= set([arg])

Одним из решений этого является просто дать ему исключение, если объект не может быть повторен. Но это не работает в моем случае использования. Другое решение - использовать что-то вроде:

import collections

def issequenceforme(obj):
    if isinstance(obj, basestring):
        return False
    return isinstance(obj, collections.Sequence)

От: Python: проверьте, является ли объект последовательностью

Но для этого требуется, чтобы эта функция была определена, что заставляет меня не хотеть ее использовать.

Похоже, что hasattr(arg, '__iter__') по-прежнему является лучшим вариантом.

Ответ 1

После разговора с некоторыми коллегами я пришел к выводу, что hasattr(arg, '__iter__') может быть приятным одним лайнером, но он далек от совершенства, как вы, ребята, также указали. Это то, с чем я столкнулся:

if isinstance(arg, basestring):
    arg= set([arg])
else:
    try:
        selection= set(arg)
    except TypeError:
        selection= set([arg])

Ответ 2

collections.Iterable гарантирует, что объект итерируется или нет (например, с помощью for x in obj), но проверка __iter__ не будет.

Строка - это итерируемый тип данных, но на Python2.x он не имеет метода __iter__.

Ответ 3

Вы также можете добавить __iter__ к своим собственным классам, поэтому любой тест, не содержащий hasattr(obj, '__iter__'), не сможет гарантировать их обнаружение. Вы не указали в вопросе о том, что вас интересуют только встроенные коллекции.

Ответ 4

collections.Sequence близок, но он возвращает True для строк.

  • collections.Iterable - для итерируемого объекта, то есть какой-либо формы контейнера, который позволяет вам перебирать объекты, которые он содержит, один за другим. Строки включены в это, так как вы можете перебирать каждый символ, это строка.
  • collections.Sequence предназначен для подмножества итерируемых объектов, которые гарантируют порядок итераций. Списки и строки возвращают True, так как порядок итераций всегда один и тот же для одного и того же списка или строки, в то время как наборы и словари возвращают False, так как вы не знаете, в каком порядке появятся внутренние объекты.

Если вы уверены, что хотите обнаружить только встроенные типы (часто считается подозрительной практикой, предпочтение отдается "утка" ) то вы можете сделать это:

if isinstance(arg, (set, list, dict, tuple)):
    print "I got a built-in iterable."
else:
    print "Hey, this ain't iterable!"

Однако решение, с которым вы столкнулись, лучше - обрабатывайте любые особые случаи (например, строки в вашем случае), а затем используйте утиную печать. Это позволяет другим использовать любой контейнер, соответствующий их потребностям, и несет ответственность за то, чтобы он соответствовал тому, что требует от вас ваш код.

Ответ 5

Вопрос: Я видел пару сообщений, рекомендующих isinstance (obj, collections.Sequence) вместо hasattr (obj, '__iter__'), чтобы определить, что-то есть список.

Чтобы определить, является ли что-то iterable, самым надежным методом является попытка создать iterator. Это определит, действительно ли объект итерабельен:

def is_iterable(obj):
    try:
        iter(obj)
    except TypeError:
        return False
    else:
        return True

Несколько менее надежный, но чистый подход заключается в том, чтобы протестировать абстрактный абстрактный базовый класс . Это проверит, имеет ли объект метод __iter __() или зарегистрирован ли он как Iterable (например, экземпляр str не имеет метода __iter __(), но он зарегистрирован как итеративный):

isinstance(obj, collections.Iterable)

Если вас интересуют списки и похожие объекты, вы можете протестировать MutableSequence ABC:

isinstance(obj, collections.MutableSequence)

Тем не менее, если вы полагаетесь на объект, имеющий конкретное поведение в виде списка, то метод Pythonic заключается в том, чтобы предположить, что этот метод присутствует и улавливает исключение, если это не так. Это называется утиная печать.