seleccione un solo elemento de una colección: Python

Creé una función de utilidad para devolver el único elemento esperado de una expresión generadora

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

¿Es esta una buena manera de hacerlo?

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

Una solución más simple es usar el desempaquetado de la tupla. Esto ya hará todo lo que desee, incluida la verificación de que contiene exactamente un elemento.

Objeto unico:

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

Demasiados elementos:

 >>> name, = (name for name in ('bob','bob') if name=='bob') Traceback (most recent call last): File "", line 1, in  ValueError: too many values to unpack 

No hay artículos:

 >>> name, = (name for name in ('fred','joe') if name=='bob') Traceback (most recent call last): File "", line 1, in  ValueError: need more than 0 values to unpack 

Para aquellos que utilizan o están interesados ​​en una biblioteca de terceros, more_itertools implementa una herramienta de este tipo con manejo de errores 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: ... 

Ver more_itertools docs para más detalles. El código fuente subyacente es similar a la respuesta aceptada.

Enfoque simple:

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

Si realmente desea un error cuando hay más de un valor, entonces necesita una función. Lo más simple que puedo pensar es ( EDITADO para trabajar con listas también):

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

¿Quieres decir?

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

¿Qué estás tratando de lograr con todo el código extra?

Eche un vistazo al método 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 "", line 1, in  StopIteration >>> 

Este módulo implementa una serie de bloques de construcción de iteradores inspirados en construcciones de los lenguajes de progtwigción Haskell y SML. Cada uno ha sido refundido en una forma adecuada para Python.

El módulo estandariza un conjunto básico de herramientas rápidas y eficientes en memoria que son útiles por sí mismas o en combinación. La estandarización ayuda a evitar los problemas de legibilidad y confiabilidad que surgen cuando muchas personas diferentes crean sus propias implementaciones ligeramente diferentes, cada una con sus propias peculiaridades y convenciones de nomenclatura.

Las herramientas están diseñadas para combinarse fácilmente entre sí. Esto facilita la construcción de herramientas más especializadas de forma sucinta y eficiente en Python puro.

Aquí está mi bash en la función one() . .next() llamada explícita .next() y utilizaría un bucle for en su lugar.

 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 

Primero, (para responder a la pregunta real), su solución funcionará bien al igual que las otras variantes propuestas.

Me gustaría añadir que, en este caso, IMO, los generadores son demasiado complicados. Si esperas tener un valor, probablemente nunca tengas suficiente para que el uso de la memoria sea una preocupación, por lo que simplemente habría usado lo obvio y mucho más claro:

 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] 

¿Qué hay de usar Python’s para … en syntax con un contador? Similar a la respuesta desconocida.

 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