выбрать один элемент из коллекции: Python - PullRequest
4 голосов
/ 23 января 2009

Я создал служебную функцию для возврата ожидаемого отдельного элемента из выражения генератора

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')

Ответы [ 8 ]

20 голосов
/ 23 января 2009

Более простое решение - использовать распаковку кортежей. Это уже сделает все, что вы хотите, включая проверку того, что он содержит ровно один элемент.

Отдельный предмет:

 >>> 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
4 голосов
/ 30 октября 2017

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

3 голосов
/ 23 января 2009

Простой подход:

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')
2 голосов
/ 23 января 2009

Вы имеете в виду?

def one( someGenerator ):
    if len(list(someGenerator)) != 1: raise Exception( "Not a Singleton" )

Что вы пытаетесь выполнить с помощью всего дополнительного кода?

1 голос
/ 23 января 2009

Во-первых, (чтобы ответить на реальный вопрос!) Ваше решение будет работать нормально, как и другие предложенные варианты.

Я бы добавил, что в этом случае генераторы 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]
1 голос
/ 23 января 2009

Вот моя попытка функции 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
1 голос
/ 23 января 2009

Ознакомьтесь с методом 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.

0 голосов
/ 31 июля 2009

Как насчет использования 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
...