Question

J'ai créé une fonction utilitaire pour renvoyer l'élément unique attendu d'une expression génératrice

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

Est-ce une bonne façon de s'y prendre?

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')
Était-ce utile?

La solution

Une solution plus simple consiste à utiliser la décompression des tuples. Cela fera déjà tout ce que vous voulez, y compris en vérifiant qu'il contient exactement un élément.

Article unique:

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

Trop d'éléments:

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

Aucun élément:

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

Autres conseils

Approche simple:

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

Si vous voulez vraiment une erreur quand il y a plus d'une valeur, alors vous avez besoin d'une fonction. Le plus simple auquel je puisse penser est ( ÉDITÉ de travailler aussi avec les listes):

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

Pour ceux qui utilisent ou s'intéressent à une bibliothèque tierce, more_itertools implémente un tel outil avec une gestion d'erreur native:

> pip install more_itertools

Code

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

Voir more_itertools docs pour plus de détails. Le code de source sous-jacente est similaire au réponse acceptée.

Consultez la méthode 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
>>> 
  

Ce module implémente un certain nombre de blocs de construction d'itérateurs inspirés des constructions des langages de programmation Haskell et SML. Chacune a été reformulée sous une forme adaptée à Python.

     

Le module standardise un ensemble principal d’outils rapides, économes en mémoire, qui sont utiles par eux-mêmes ou en combinaison. La normalisation permet d’éviter les problèmes de lisibilité et de fiabilité qui se posent lorsque de nombreuses personnes différentes créent leurs propres implémentations légèrement différentes, chacune avec ses propres particularités et conventions de dénomination.

     

Les outils sont conçus pour se combiner facilement. Cela facilite la construction d’outils plus spécialisés de manière succincte et efficace en Python pur.

Voulez-vous dire?

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

Qu'est-ce que vous essayez d'accomplir avec tout le code supplémentaire?

Voici mon essai sur la fonction one () . J'éviterais l'appel .next () explicite et utiliserais une boucle for à la place.

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

Tout d'abord, (pour répondre à la question posée!), votre solution fonctionnera correctement, de même que les autres variantes proposées.

J'ajouterais que dans ce cas, OMI, les générateurs sont excessivement compliqués. Si vous vous attendez à avoir une valeur, vous n'aurez probablement jamais assez de mémoire pour que l'utilisation de la mémoire soit un problème. J'aurais donc utilisé l'évidence plus claire:

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]

Pourquoi ne pas utiliser Python's pour la syntaxe avec un compteur? Semblable à la réponse 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top