hasattr (obj, '__iter__') против коллекций - PullRequest
12 голосов
/ 26 августа 2011

Я видел пару сообщений, рекомендующих 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__') все еще лучший вариант для тестирования, если что-то повторяется?

РЕДАКТИРОВАТЬ: Меня интересует только обнаружение встроенных типов: dict, list и set. ( РЕДАКТИРОВАТЬ: это глупо:))

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

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__') по-прежнему лучший вариант.

Ответы [ 5 ]

5 голосов
/ 26 августа 2011

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

Строка является итеративным типом данных, но в Python2.x она не имеет __iter__ метода.

2 голосов
/ 27 августа 2011

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!"

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

2 голосов
/ 26 августа 2011

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

1 голос
/ 01 июня 2013

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

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

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 может предположить, что метод присутствует, и перехватить исключение, если это не так. Это называется Утка набрав .

1 голос
/ 27 августа 2011

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

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