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