web-dev-qa-db-fra.com

Python idiome pour retourner le premier élément ou aucun

Je suis sûr qu'il existe un moyen plus simple de faire cela qui ne me vient tout simplement pas à l'esprit.

J'appelle un tas de méthodes qui renvoient une liste. La liste peut être vide. Si la liste n'est pas vide, je souhaite renvoyer le premier élément. sinon, je veux renvoyer Aucun. Ce code fonctionne:

my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None

Il me semble qu’il devrait exister un langage simple à une ligne pour le faire, mais je ne peux pas y penser. Y a-t-il?

Edit:

La raison pour laquelle je recherche ici une expression d'une ligne n'est pas parce que j'aime un code extrêmement laconique, mais parce que je dois écrire beaucoup de code comme celui-ci:

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

Ce que j'aimerais faire peut certainement être accompli avec une fonction (et sera probablement):

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

J'ai posté la question parce que je suis souvent surpris par ce que des expressions simples dans Python peuvent faire, et je pensais qu'écrire une fonction était une chose stupide à faire s'il existait une simple expression qui pourrait faire l'affaire. Mais en voyant ces réponses, il semble qu'une fonction soit la solution simple.

229
Robert Rossney

Python 2.6+

next(iter(your_list), None)

Si your_list peut être None:

next(iter(your_list or []), None)

Python 2.4

def get_first(iterable, default=None):
    if iterable:
        for item in iterable:
            return item
    return default

Exemple:

x = get_first(get_first_list())
if x:
    ...
y = get_first(get_second_list())
if y:
    ...

Une autre option consiste à intégrer la fonction ci-dessus:

for x in get_first_list() or []:
    # process x
    break # process at most one item
for y in get_second_list() or []:
    # process y
    break

Pour éviter break, vous pouvez écrire:

for x in yield_first(get_first_list()):
    x # process x
for y in yield_first(get_second_list()):
    y # process y

Où:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return
158
jfs

Le meilleur moyen est le suivant:

a = get_list()
return a[0] if a else None

Vous pouvez aussi le faire en une ligne, mais le programmeur a beaucoup de difficulté à lire:

return (get_list()[:1] or [None])[0]
198
efotinis
(get_list() or [None])[0]

Cela devrait fonctionner.

BTW je n'ai pas utilisé la variable list, car cela écrase la fonction intégrée list().

Edit: J'avais une version un peu plus simple, mais fausse ici plus tôt.

65
recursive

La manière la plus idiomatique de python consiste à utiliser le next () sur un itérateur puisque la liste est itérable . comme ce que @ J.F.Sebastian a mis dans le commentaire du 13 décembre 2011.

next(iter(the_list), None) Ceci retourne None si the_list est vide. voir next () Python 2.6 +

ou si vous êtes sûr que the_list n'est pas vide:

iter(the_list).next() see iterator.next () Python 2.2 +

31
Devy

La solution du PO est presque là, il y a juste quelques choses pour le rendre plus Pythonic.

D'une part, il n'est pas nécessaire de connaître la longueur de la liste. Les listes vides dans Python sont évaluées à False lors d'une vérification if. Dites simplement

if list:

De plus, c’est une très mauvaise idée d’affecter des variables qui se chevauchent avec des mots réservés. "liste" est un mot réservé en Python.

Alors changeons cela en

some_list = get_list()
if some_list:

Un point vraiment important que beaucoup de solutions manquent ici est que all Python fonctions/méthodes ne renvoie Aucun par défaut . Essayez ce qui suit ci-dessous.

def does_nothing():
    pass

foo = does_nothing()
print foo

Sauf si vous devez renvoyer None pour mettre fin à une fonction plus tôt, il est inutile de renvoyer explicitement None. Assez succinctement, retournez simplement la première entrée, si elle existe.

some_list = get_list()
if some_list:
    return list[0]

Et enfin, peut-être que cela était implicite, mais juste pour être explicite (parce que explicite vaut mieux que implicite ), vous ne devriez pas avoir votre fonction extraire la liste d'une autre fonction; il suffit de le passer en paramètre. Donc, le résultat final serait

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

Comme je le disais, le PO était presque là et quelques touches lui donnent la saveur Python que vous recherchez.

10
gotgenes

Si vous essayez d’arracher la première chose (ou Aucune) à une liste de compréhension, vous pouvez passer à un générateur pour le faire comme ceci:

next((x for x in blah if cond), None)

Pro: fonctionne si blah n'est pas indexable. Con: c'est une syntaxe inconnue. C'est utile lors du piratage et du filtrage de choses dans ipython.

10
Aidan Kane

idiome Python pour retourner le premier élément ou aucun?

L’approche la plus pythonique est ce que la réponse la plus votée a démontré, et c’est la première chose qui m’est venue à l’esprit lorsque j'ai lu la question. Voici comment l'utiliser, tout d'abord si la liste éventuellement vide est passée à une fonction:

def get_first(l): 
    return l[0] if l else None

Et si la liste est renvoyée par une fonction get_list:

l = get_list()
return l[0] if l else None

Autres moyens démontrés de le faire ici, avec explications

for

Quand j'ai commencé à essayer de trouver des moyens intelligents de le faire, c'est la deuxième chose à laquelle j'ai pensé:

for item in get_list():
    return item

Cela suppose que la fonction se termine ici et renvoie implicitement None si get_list renvoie une liste vide. Le code explicite ci-dessous est exactement équivalent:

for item in get_list():
    return item
return None

if some_list

Ce qui suit a également été proposé (j'ai corrigé le nom de variable incorrect) qui utilise également le None implicite. Ce serait préférable à ce qui précède, car il utilise la vérification logique au lieu d'une itération qui peut ne pas se produire. Cela devrait être plus facile à comprendre immédiatement ce qui se passe. Mais si nous écrivons pour des raisons de lisibilité et de maintenabilité, nous devrions également ajouter le return None explicite à la fin:

some_list = get_list()
if some_list:
    return some_list[0]

découpez or [None] et sélectionnez zeroth index

Celui-ci est également dans la réponse la plus votée:

return (get_list()[:1] or [None])[0]

La tranche n'est pas nécessaire et crée une liste supplémentaire d'un élément en mémoire. Ce qui suit devrait être plus performant. Pour expliquer, or renvoie le deuxième élément si le premier est False dans un contexte booléen. Ainsi, si get_list renvoie une liste vide, l'expression contenue dans les parenthèses retournera une liste avec ' Aucune ', qui sera ensuite accessible par l'index 0:

return (get_list() or [None])[0]

Le prochain utilise le fait que et renvoie le second élément si le premier est True dans un contexte booléen, et puisqu'il référence deux fois à my_list, il ne vaut pas mieux que l'expression ternaire (et techniquement, ce n'est pas un one-liner) :

my_list = get_list() 
return (my_list and my_list[0]) or None

next

Nous avons ensuite l’utilisation intelligente suivante des fonctions intégrées next et iter

return next(iter(get_list()), None)

Pour expliquer, iter renvoie un itérateur avec une méthode .next. (.__next__ dans Python 3.) Ensuite, la fonction intégrée next appelle cette méthode .next et si l'itérateur est épuisé, renvoie la valeur par défaut que nous donnons, None.

expression ternaire redondante (a if b else c) et retour

La proposition ci-dessous a été proposée, mais l'inverse serait préférable, car la logique est généralement mieux comprise dans le positif que dans le négatif. Puisque get_list est appelé deux fois, à moins que le résultat ne soit mémorisé d'une manière ou d'une autre, les performances seraient médiocres:

return None if not get_list() else get_list()[0]

Le meilleur inverse:

return get_list()[0] if get_list() else None

Mieux encore, utilisez une variable locale pour que get_list ne soit appelé qu'une seule fois et que la solution Pythonic recommandée soit d'abord discutée:

l = get_list()
return l[0] if l else None
2
Aaron Hall

En ce qui concerne les idiomes, il existe une recette des outils informatiques appelée nth.

De recettes itertools:

_def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)
_

Si vous voulez des solutions uniques, pensez à installer une bibliothèque qui implémente cette recette pour vous, par exemple. more_itertools :

_import more_itertools as mit

mit.nth([3, 2, 1], 0)
# 3

mit.nth([], 0)                                             # default is `None`
# None
_

Un autre outil est disponible qui renvoie uniquement le premier élément, appelé more_itertools.first .

_mit.first([3, 2, 1])
# 3

mit.first([], default=None)
# None
_

Ces outils informatiques sont génériques pour tous les itératifs, pas seulement pour les listes.

2
pylang

Franchement, je ne pense pas qu’il existe un meilleur langage: votre langage est clair et concis - vous n’avez besoin de rien de "meilleur". Peut-être, mais c'est vraiment une question de goût, vous pourriez changer if len(list) > 0: avec if list: - une liste vide sera toujours évaluée à False.

Sur une note connexe, Python est pas Perl (sans jeu de mots!), Vous n'avez pas à obtenir le code le plus cool possible.
En fait, le pire code que j’ai vu en Python était également très cool :-) et totalement immuable.

En passant, la plupart des solutions que j'ai vues ici ne prennent pas en compte la liste False (0, par exemple, chaîne vide ou zéro). Dans ce cas, elles ne renvoient toutes ni aucun, ni l'élément correct.

2
rob
for item in get_list():
    return item
2
A. Coady

Par curiosité, j’ai programmé deux solutions. La solution qui utilise une instruction return pour mettre fin prématurément à une boucle for est légèrement plus coûteuse sur ma machine avec Python 2.5.1, je suppose que cela a à voir avec la configuration de l'itérable.

import random
import timeit

def index_first_item(some_list):
    if some_list:
        return some_list[0]


def return_first_item(some_list):
    for item in some_list:
        return item


empty_lists = []
for i in range(10000):
    empty_lists.append([])

assert empty_lists[0] is not empty_lists[1]

full_lists = []
for i in range(10000):
    full_lists.append(list([random.random() for i in range(10)]))

mixed_lists = empty_lists[:50000] + full_lists[:50000]
random.shuffle(mixed_lists)

if __== '__main__':
    ENV = 'import firstitem'
    test_data = ('empty_lists', 'full_lists', 'mixed_lists')
    funcs = ('index_first_item', 'return_first_item')
    for data in test_data:
        print "%s:" % data
        for func in funcs:
            t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
                func, data), ENV)
            times = t.repeat()
            avg_time = sum(times) / len(times)
            print "  %s:" % func
            for time in times:
                print "    %f seconds" % time
            print "    %f seconds avg." % avg_time

Ce sont les timings que j'ai eu:

 empty_lists: 
 index_first_item: 
 0.748353 secondes 
 0.741086 secondes 
 0.741191 secondes 
 0.743543 secondes moyen 
 return_first_item : 
 0.785511 secondes 
 0.822178 secondes 
 0.782846 secondes 
 0.796845 secondes moyennes 
 Listes complètes: 
 Index_first_item: 
 0.762618 secondes 
 0.788040 secondes 
 0.786849 secondes 
 0.779169 secondes moyen 
 Return_first_item: 
 0.802735 secondes 
 0.878706 secondes 
 0.808781 secondes 
 0.830074 secondes moyen 
 Listes mixtes: 
 Index_first_item: 
 0.791129 secondes 
 0.743526 secondes 
 0.744441 secondes 
 0,759699 secondes moyen 
 Return_first_item: 
 0,784801 secondes 
 0.785146 secondes 
 0.840193 secondes 
 0.803380 secondes moyen 
1
gotgenes
try:
    return a[0]
except IndexError:
    return None
0
limscoder

Plusieurs personnes ont suggéré de faire quelque chose comme ceci:

list = get_list()
return list and list[0] or None

Cela fonctionne dans de nombreux cas, mais cela ne fonctionnera que si list [0] n'est pas égal à 0, False ou une chaîne vide. Si list [0] est 0, False ou une chaîne vide, la méthode ne renverra pas correctement None.

J'ai créé ce bogue dans mon propre code une fois de trop!

0
Clint Miller

Que dis-tu de ça:

(my_list and my_list[0]) or None

Note: Cela devrait fonctionner correctement pour les listes d'objets, mais cela pourrait donner une réponse incorrecte en cas de liste de nombres ou de chaînes selon les commentaires ci-dessous.

0
VitalyB
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

print head(xrange(42, 1000)  # 42
print head([])               # None

BTW: Je retravailler votre flux de programme général dans quelque chose comme:

lists = [
    ["first", "list"],
    ["second", "list"],
    ["third", "list"]
]

def do_something(element):
    if not element:
        return
    else:
        # do something
        pass

for li in lists:
    do_something(head(li))

(Éviter la répétition autant que possible)

0
ttepasse