variabile Linguaggio Python per restituire il primo oggetto o Nessuno




stampare variabile python (19)

Sono sicuro che c'è un modo più semplice di fare questo che non mi sta succedendo.

Sto chiamando un sacco di metodi che restituiscono una lista. L'elenco potrebbe essere vuoto. Se l'elenco non è vuoto, voglio restituire il primo elemento; altrimenti, voglio restituire None. Questo codice funziona:

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

Mi sembra che ci debba essere un semplice idioma di una sola linea per farlo, ma per la vita di me non riesco a pensarci. È lì?

Modificare:

Il motivo per cui sto cercando un'espressione a una riga non è che mi piaccia il codice incredibilmente tergente, ma perché sto scrivendo un sacco di codice come questo:

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

Quello che mi piacerebbe fare può certamente essere realizzato con una funzione (e probabilmente lo sarà):

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

Ho postato la domanda perché sono spesso sorpreso da ciò che le espressioni semplici in Python possono fare, e ho pensato che scrivere una funzione fosse una cosa stupida da fare se esistesse un'espressione semplice che potesse fare il trucco. Ma vedendo queste risposte, sembra che una funzione sia la soluzione semplice.


if mylist != []:

       print(mylist[0])

   else:

       print(None)

non è il python idiomatico equivalente agli operatori ternari in stile C.

cond and true_expr or false_expr

vale a dire.

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

E che dire: next(iter(get_list()), None) ? Potrebbe non essere il più veloce qui, ma è standard (a partire da Python 2.6) e succinto.


Usando l'and-or trick:

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

Per curiosità, ho eseguito i tempi su due delle soluzioni. La soluzione che utilizza un'istruzione return per terminare prematuramente un ciclo for è leggermente più costosa sulla mia macchina con Python 2.5.1, sospetto che questo abbia a che fare con la configurazione del iterable.

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 __name__ == '__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

Questi sono i tempi che ho ottenuto:

empty_lists:
  index_first_item:
    0.748353 seconds
    0.741086 seconds
    0.741191 seconds
    0.743543 seconds avg.
  return_first_item:
    0.785511 seconds
    0.822178 seconds
    0.782846 seconds
    0.796845 seconds avg.
full_lists:
  index_first_item:
    0.762618 seconds
    0.788040 seconds
    0.786849 seconds
    0.779169 seconds avg.
  return_first_item:
    0.802735 seconds
    0.878706 seconds
    0.808781 seconds
    0.830074 seconds avg.
mixed_lists:
  index_first_item:
    0.791129 seconds
    0.743526 seconds
    0.744441 seconds
    0.759699 seconds avg.
  return_first_item:
    0.784801 seconds
    0.785146 seconds
    0.840193 seconds
    0.803380 seconds avg.

for item in get_list():
    return item

Cosa ne pensi di questo:

(my_list and my_list[0]) or None

Nota: questo dovrebbe funzionare bene per gli elenchi di oggetti, ma potrebbe restituire una risposta errata in caso di numero o lista di stringhe per i commenti sottostanti.


Parlando francamente, non penso che ci sia un linguaggio migliore: il tuo è chiaro e conciso, non c'è bisogno di nulla di "migliore". Forse, ma questa è davvero una questione di gusti, potresti cambiare if len(list) > 0: con if list: - una lista vuota sarà sempre valutata a False.

Su una nota correlata, Python non è Perl (non è un gioco di parole!), Non è necessario ottenere il codice più bello possibile.
In realtà, il codice peggiore che ho visto in Python, era anche molto bello :-) e completamente non-mantenibile.

A proposito, la maggior parte della soluzione che ho visto qui non prende in considerazione quando list [0] restituisce False (es. Stringa vuota o zero) - in questo caso restituiscono tutti None e non l'elemento corretto.


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

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

BTW: mi piacerebbe rielaborare il flusso del programma generale in qualcosa del genere:

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

(Evitare la ripetizione quando possibile)


try:
    return a[0]
except IndexError:
    return None

Probabilmente non è la soluzione più veloce, ma nessuno ha menzionato questa opzione:

dict(enumerate(get_list())).get(0)

se get_list() può restituire None puoi usare:

dict(enumerate(get_list() or [])).get(0)

vantaggi:

-una linea

get_list() chiami get_list() una volta sola

-facile da capire


Python 2.6+

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

Esempio:

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

Un'altra opzione è quella di inline la funzione di cui sopra:

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

Per evitare le break potresti scrivere:

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

Dove:

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

Il mio caso d'uso era solo per impostare il valore di una variabile locale.

Personalmente ho trovato la prova e tranne la pulizia di stile da leggere

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

di affettare una lista.

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item

La soluzione dell'OP è quasi lì, ci sono solo alcune cose per renderlo più Pythonic.

Per uno, non c'è bisogno di ottenere la lunghezza della lista. Le liste vuote in Python valutano False in un controllo if. Dì semplicemente

if list:

Inoltre, è una pessima idea assegnare a variabili che si sovrappongono a parole riservate. "list" è una parola riservata in Python.

Quindi cambiamo

some_list = get_list()
if some_list:

Un punto davvero importante che molte soluzioni qui mancano è che tutte le funzioni / metodi Python restituiscono None per impostazione predefinita . Prova quanto segue di seguito.

def does_nothing():
    pass

foo = does_nothing()
print foo

A meno che non sia necessario restituire None per terminare anticipatamente una funzione, non è necessario restituire esplicitamente None. Abbastanza sinteticamente, basta restituire la prima voce, se esiste.

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

E alla fine, forse questo era implicito, ma solo per essere esplicito (perché l' esplicito è meglio che implicito ), non dovresti avere la tua funzione per ottenere l'elenco da un'altra funzione; basta passarlo come parametro. Quindi, il risultato finale sarebbe

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

my_list = get_list()
first_item = get_first_item(my_list)

Come ho detto, l'OP era quasi arrivato, e solo pochi tocchi gli danno il sapore Python che stai cercando.


Per il gusto di farlo, ecco un'altra possibilità.

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

Vantaggio: questo metodo gestisce il caso in cui get_list è None, senza utilizzare try / except o assignment. A mia conoscenza, nessuna delle implementazioni di cui sopra può gestire questa possibilità

Downfalls: get_list () viene chiamato due volte, abbastanza inutilmente, specialmente se l'elenco è lungo e / o creato quando viene chiamata la funzione.

La verità è che, a mio parere, è più "Pythonico" fornire un codice che sia leggibile rispetto a quello di creare un solo liner solo perché è possibile :) Devo ammettere che sono colpevole molte volte di codice Python inutilmente compattante solo perché Sono così impressionato da quanto piccolo possa rendere un aspetto complesso della funzione :)

Modifica: come utente "hasen j" commentato di seguito, l'espressione condizionale di cui sopra è nuova in Python 2.5, come descritto qui: https://docs.python.org/whatsnew/2.5.html#pep-308 . Grazie, hasen!


Idioma Python per restituire il primo oggetto o nessuno?

L'approccio più pitonico è quello che la risposta più spinta ha dimostrato, ed è stata la prima cosa che mi è venuta in mente quando ho letto la domanda. Ecco come usarlo, prima se la lista eventualmente vuota è passata in una funzione:

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

E se la lista viene restituita da una funzione get_list :

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

Altri modi hanno dimostrato di farlo qui, con spiegazioni

for

Quando ho iniziato a provare a pensare a modi intelligenti per farlo, questa è la seconda cosa che ho pensato:

for item in get_list():
    return item

Ciò presuppone che la funzione finisca qui, restituendo implicitamente None se get_list restituisce una lista vuota. Il codice sotto esplicito è esattamente equivalente:

for item in get_list():
    return item
return None

if some_list

È stato anche proposto il seguente (ho corretto il nome della variabile errata) che usa anche l'implicito None . Questo sarebbe preferibile a quanto sopra, in quanto utilizza il controllo logico anziché un'iterazione che potrebbe non avvenire. Questo dovrebbe essere più facile capire immediatamente cosa sta succedendo. Ma se stiamo scrivendo per leggibilità e manutenibilità, dovremmo aggiungere anche il return None esplicito return None alla fine:

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

slice or [None] e selezionare l'indice zeroth

Questo è anche nella risposta più votata:

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

La sezione non è necessaria e crea in memoria un elenco di un elemento aggiuntivo. Il seguente dovrebbe essere più performante. Per spiegare, or restituisce il secondo elemento se il primo è False in un contesto booleano, quindi se get_list restituisce una lista vuota, l'espressione contenuta nelle parentesi restituirà una lista con 'None', a cui sarà quindi possibile accedere dall'indice 0 :

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

Il prossimo usa il fatto che e restituisce il secondo elemento se il primo è True in un contesto booleano, e poiché fa riferimento a my_list due volte, non è migliore dell'espressione ternaria (e tecnicamente non è un one-liner):

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

next

Quindi abbiamo il seguente uso intelligente del builtin next e iter

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

Per spiegare, iter restituisce un iteratore con un metodo .next . ( .__next__ in Python 3.) Quindi il builtin chiama .next metodo .next e se l'iteratore è esaurito, restituisce il valore predefinito che diamo, None .

espressione ternaria ridondante ( a if b else c ) e indietro

È stato proposto il seguente, ma sarebbe preferibile l'inverso, in quanto la logica è solitamente meglio compresa nel positivo anziché nel negativo. Poiché get_list viene chiamato due volte, a meno che il risultato non sia memoized in qualche modo, questo si comporta male:

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

Il migliore inverso:

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

Ancora meglio, usa una variabile locale in modo che get_list venga chiamato solo una volta e hai prima discusso la soluzione Pythonic raccomandata:

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

Per quanto riguarda gli idiomi, c'è una ricetta itertools chiamata nth .

Dalle ricette itertools:

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

Se vuoi one-liner, considera l'installazione di una libreria che implementa questa ricetta per te, ad es. more_itertools :

import more_itertools as mit

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

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

È disponibile un altro strumento che restituisce solo il primo elemento, chiamato more_itertools.first .

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

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

Questi itertools scalano genericamente per ogni iterabile, non solo per le liste.


Diverse persone hanno suggerito di fare qualcosa del genere:

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

Questo funziona in molti casi, ma funzionerà solo se l'elenco [0] non è uguale a 0, False o una stringa vuota. Se l'elenco [0] è 0, False o una stringa vuota, il metodo restituirà in modo errato Nessuno.

Ho creato questo bug nel mio codice una troppe volte!


Il modo migliore è questo:

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

Puoi anche farlo in una riga, ma è molto più difficile per il programmatore leggere:

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




python-2.4