Pergunta

Eu criei uma função de utilidade para retornar o item único esperado de um gerador de expressão

print one(name for name in ('bob','fred') if name=='bob')

Esta é uma boa maneira de ir sobre ele?

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')
Foi útil?

Solução

A solução mais simples é a descompactação uso tupla. Isso já vai fazer tudo o que quiser, incluindo a verificação de que ele contém exatamente um item.

único item:

 >>> name, = (name for name in ('bob','fred') if name=='bob')
 >>> name
 'bob'

Muitos itens:

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

Não há itens:

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

Outras dicas

abordagem simples:

print (name for name in ('bob', 'fred') if name == 'bob').next()

Se você realmente quer um erro quando há mais de um valor, então você precisa de uma função. O mais simples que posso pensar é ( EDITADO para trabalhar com listas também):

def one(iterable):
    it = iter(iterable)
    val = it.next()
    try:
        it.next()
    except StopIteration:
        return val
    else:
        raise Exception('More than one value')

Para aqueles que usam ou interessados ??em uma biblioteca de terceiros, more_itertools implementos como uma ferramenta com a manipulação de erro nativo:

> pip install more_itertools

Código

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 docs para mais detalhes. O código-fonte subjacente é semelhante ao resposta aceita.

Você quer dizer?

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

O que você está tentando realizar com todo o código extra?

Aqui está a minha tentativa em função one(). Gostaria de evitar a chamada .next() explícita e usar um loop for em vez disso.

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

Em primeiro lugar, (para responder à pergunta real!) Sua solução irá funcionar bem como vão as outras variantes proposto.

Gostaria de acrescentar que, neste caso, IMO, geradores são excessivamente complicado. Se você espera ter um valor, você provavelmente nunca tem o suficiente para o uso da memória a ser uma preocupação, então eu teria utilizado apenas o óbvio e muito mais clara:

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]

Que tal usar Python para .. na sintaxe com um contador? Semelhante a resposta de unbeknown.

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
scroll top