выбрать один элемент из коллекции:Питон
Вопрос
Я создал служебную функцию для возврата ожидаемого одного элемента из выражения генератора.
print one(name for name in ('bob','fred') if name=='bob')
Это хороший способ сделать это?
def one(g):
try:
val = g.next()
try:
g.next()
except StopIteration:
return val
else:
raise Exception('Too many values')
except StopIteration:
raise Exception('No values')
Решение
Более простое решение - использовать распаковку кортежей. Это уже сделает все, что вы хотите, включая проверку того, что он содержит ровно один элемент.
Один элемент:
>>> name, = (name for name in ('bob','fred') if name=='bob')
>>> name
'bob'
Слишком много предметов.
>>> name, = (name for name in ('bob','bob') if name=='bob')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: too many values to unpack
Нет записей:
>>> name, = (name for name in ('fred','joe') if name=='bob')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack
Другие советы
Простой подход:
print (name for name in ('bob', 'fred') if name == 'bob').next()
Если вам действительно нужна ошибка, когда существует более одного значения, вам нужна функция. Самое простое, о чем я могу подумать ( EDITED для работы со списками):
def one(iterable):
it = iter(iterable)
val = it.next()
try:
it.next()
except StopIteration:
return val
else:
raise Exception('More than one value')
Для тех, кто использует стороннюю библиотеку или интересуется ею, more_itertools
реализует такой инструмент со встроенной обработкой ошибок:
> pip install more_itertools
Код
import more_itertools as mit
mit.one(name for name in ("bob", "fred") if name == "bob")
# 'bob'
mit.one(name for name in ("bob", "fred", "bob") if name == "bob")
# ValueError: ...
mit.one(name for name in () if name == "bob")
# ValueError: ...
Видеть more_itertools
документы для получения подробной информации.А базовый исходный код аналогично принятому ответу.
Ознакомьтесь с методом itertools.islice () . . р>
>>> i2=itertools.islice((name for name in ('bob','fred') if name=='bob'),0,1,1)
>>> i2.next()
'bob'
>>> i2.next()
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
StopIteration
>>>
Этот модуль реализует ряд строительных блоков итераторов, основанных на конструкциях из языков программирования Haskell и SML. Каждый был преобразован в форму, подходящую для Python.
Модуль стандартизирует базовый набор быстрых, эффективных с точки зрения памяти инструментов, которые полезны сами по себе или в сочетании. Стандартизация помогает избежать проблем с удобочитаемостью и надежностью, которые возникают, когда многие разные люди создают свои собственные слегка изменяющиеся реализации, каждая со своими причудами и соглашениями об именах.
Инструменты предназначены для быстрого объединения друг с другом. Это позволяет легко и эффективно создавать более специализированные инструменты в чистом Python.
Вы имеете в виду?
def one( someGenerator ):
if len(list(someGenerator)) != 1: raise Exception( "Not a Singleton" )
Что вы пытаетесь достичь с помощью всего дополнительного кода? Р>
Вот моя попытка в функции one()
. Я бы избегал явного вызова .next()
и использовал бы цикл for.
def one(seq):
counter = 0
for elem in seq:
result = elem
counter += 1
if counter > 1:
break
if counter == 0:
raise Exception('No values')
elif counter > 1:
raise Exception('Too many values')
return result
Во-первых, (чтобы ответить на реальный вопрос!) ваше решение будет работать нормально, как и другие предложенные варианты.
Я бы добавил, что в этом случае генераторы IMO слишком сложны. Если вы ожидаете, что у вас будет одно значение, вам, вероятно, никогда не хватит, чтобы использование памяти было проблемой, поэтому я бы просто использовал очевидное и гораздо более понятное:
children = [name for name in ('bob','fred') if name=='bob']
if len(children) == 0:
raise Exception('No values')
elif len(children) > 1:
raise Exception('Too many values')
else:
child = children[0]
Как насчет использования Python для .. в синтаксисе со счетчиком? Похоже на неизвестный ответ.
def one(items):
count = 0
value = None
for item in items:
if count:
raise Exception('Too many values')
count += 1
value = item
if not count:
raise Exception('No values')
return value