Domanda

Sono sicuro che c'è un modo più semplice di fare questo semplicemente non è che si verificano a me.

Sto chiamando un sacco di metodi che restituiscono un elenco.L'elenco è vuoto.Se la lista non è vuota, voglio tornare il prima voce;in caso contrario, voglio tornare Nessuno.Questo codice funziona:

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

Mi sembra che ci dovrebbe essere una semplice linea idioma per fare questo, ma per la vita di me non riesco a pensare di esso.C'è?

Edit:

Il motivo che sto cercando una riga di espressione qui non è che mi piace incredibilmente terso di codice, ma perché mi sto divertendo a scrivere 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 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 sorpresi da ciò che semplici espressioni in Python può fare, e ho pensato che scrivere una funzione che era una cosa stupida da fare, se c'è una semplice espressione potrebbe fare il trucco.Ma vedendo queste risposte, mi sembra una funzione è la soluzione più semplice.

È stato utile?

Soluzione

Python 2.6 +

next(iter(your_list), None)

Se your_list può essere 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

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 break si potrebbe 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

Altri suggerimenti

Il modo migliore è questa:

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

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

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

Che dovrebbe funzionare.

A proposito non ho usato la variabile list, perché che sovrascrive la funzione built-list().

Modifica:. Ho avuto una versione leggermente più semplice, ma male qui in precedenza

Il pitone modo più idiomatico è quello di utilizzare il successivo () su un iteratore dato lista è iterabile . proprio come quello che @ J.F.Sebastian mettere nel commento 13 dicembre 2011.

next(iter(the_list), None) Questo restituisce None se the_list è vuoto. vedi successivo () Python 2.6+

o se si sa per certo iter(the_list).next() non è vuoto:

<=> vedi iterator.next () Python 2.2 +

La soluzione del PO è quasi arrivati, ci sono solo alcune delle cose per renderlo più Pythonic.

Per uno, non c'è bisogno di ottenere la lunghezza della lista. liste vuote in Python restituiscono False in caso di controllo. Basta semplicemente dire

if list:

Inoltre, è un'idea molto male per assegnare alle variabili che si sovrappongono con le parole riservate. "Lista" è una parola riservata in Python.

Quindi cerchiamo di cambiare la situazione a

some_list = get_list()
if some_list:

Un punto molto importante che un sacco di soluzioni qui manca è che tutte le funzioni Python / metodi restituiscono Nessuno di default . Provare quanto segue qui sotto.

def does_nothing():
    pass

foo = does_nothing()
print foo

A meno che non è necessario tornare Nessuno per interrompere una funzione in anticipo, è necessario tornare in modo esplicito Nessuno. Molto sinteticamente, basta tornare la prima voce, dovrebbe esistere.

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

E infine, forse questo era implicito, ma solo per essere esplicito (perché esplicito è meglio di implicita ), non si dovrebbero avere la funzione ottieni la lista 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 arrivati, e pochi tocchi dare il sapore di Python che stai cercando.

Se si trova a cercare di cogliere la prima cosa (o nessuno) da un elenco di comprensione è possibile passare ad un generatore di farlo come:

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

Pro: funziona se bla non è indicizzabile Con: è la sintassi familiare. E 'utile, mentre l'hacking in giro e roba filtrante in ipython però.

for item in get_list():
    return item

Francamente, non credo che ci sia un idioma meglio: il vostro è chiaro e conciso - non c'è bisogno per qualsiasi cosa "migliore". Forse, ma questo è davvero una questione di gusto, è possibile cambiare if len(list) > 0: con if list: -. Un elenco vuoto sarà sempre valutare False

In una nota correlata, Python è non Perl (no pun intended!), Non dovete per ottenere il codice più cool possibile.
In realtà, il codice peggiore che ho visto in Python, è stato anche :-) molto fresco e del tutto impossibile da mantenere.

Tra l'altro, la maggior parte della soluzione che ho visto qui non prendere in considerazione quando la lista [0] restituisce False (ad esempio, una stringa vuota, o pari a zero) - in questo caso, tutti di ritorno Nessuno e non l'elemento corretto .

  

Python linguaggio per tornare prima voce o None?

L'approccio più Pythonic è ciò che la risposta più upvoted dimostrato, ed è stata la prima cosa a venire in mente quando ho letto la domanda. Ecco come usarlo, prima se la lista eventualmente vuoto viene passato in una funzione:

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

E se la lista viene restituito da una funzione get_list:

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

Altri modi dimostrato di fare questo qui, con spiegazioni

for

Quando ho cominciato cercando di pensare a modi intelligenti per farlo, questa è la seconda cosa che ho pensato:

for item in get_list():
    return item

Questa presuppone la funzione finisce qui, implicitamente tornando None se if some_list restituisce una lista vuota. Il codice qui sotto esplicita è esattamente equivalente:

for item in get_list():
    return item
return None

return None

È stato inoltre proposto il seguente (ho corretto il nome della variabile errato) che utilizza anche l'implicito or [None]. Questo sarebbe preferibile a quanto sopra, in quanto utilizza il controllo logica invece di un'iterazione che non può accadere. Questo dovrebbe essere più facile da capire subito cosa sta succedendo. Ma se stiamo scrivendo per migliorare la leggibilità e la manutenibilità, dovremmo anche aggiungere il or esplicito alla fine:

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

fetta False e selezionare indice zeroth

Questo è anche il più up-risposta votato:

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

La fetta non è necessaria, e crea un elenco di un elemento aggiuntivo nella memoria. Il seguente dovrebbe essere più performante. Per spiegare, 0 restituisce il secondo elemento, se il primo è True in un contesto booleano, quindi se next restituisce un elenco vuoto, l'espressione contenuta tra parentesi restituirà un elenco di 'Nessuno', che sarà poi essere raggiunto dall'indice iter:

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

Il prossimo si usa il fatto che e restituisce la seconda voce se il primo è .next in un contesto booleano, e dal momento che fa riferimento a my_list due volte, non è meglio che l'espressione ternaria (e tecnicamente non è un one-liner ):

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

.__next__

Poi abbiamo la seguente uso intelligente del builtin a if b else c e <=>

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

Per spiegare, <=> restituisce un iteratore con un metodo <=>. (<=> in Python 3.) Quindi il comando incorporato <=> chiama che <=> metodo, e se l'iteratore è esaurito, restituisce il valore predefinito che diamo, <=>.

ridondante espressione ternaria (<=>) e girando di nuovo

Il sotto è stato proposto, ma l'inverso sarebbe preferibile, la logica è solitamente meglio compresa alla positivo invece del negativo. Dal momento che <=> è chiamato due volte, a meno che il risultato è memoized, in qualche modo, questo sarebbe scarso rendimento:

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

L'inverso meglio:

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

Ancora meglio, utilizzare una variabile locale in modo che <=> è chiamato una sola volta, e si ha la soluzione consigliata Pythonic prima discusso:

<*>

Per quanto riguarda idiomi, c'è un itertools ricetta chiamato nth.

Da itertools ricette:

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

Se si desidera one-liners, è consigliabile installare una libreria che implementa questa ricetta per voi, per esempio more_itertools :

import more_itertools as mit

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

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

Un altro strumento è disponibile che solo restituisce il primo elemento, chiamato more_itertools.first .

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

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

Queste itertools scala genericamente per qualsiasi iterabile, non solo per le liste.

Per curiosità, ho corso temporizzazioni su due delle soluzioni. La soluzione che utilizza l'istruzione return per terminare prematuramente un ciclo for è un po 'più costosa sulla mia macchina con Python 2.5.1, ho il sospetto che questo ha a che fare con la creazione del iterabile.

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.
try:
    return a[0]
except IndexError:
    return None
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

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

A proposito: mi piacerebbe rielaborare il flusso generale del programma in qualcosa di simile a questo:

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

(Evitando ripetizione quando possibile)

Che ne dite di questo:

(my_list and my_list[0]) or None

Nota: Questo dovrebbe funzionare bene per le liste di oggetti, ma potrebbe tornare risposta non corretta in caso di numero o di un elenco di stringa per i commenti qui sotto

.

Si potrebbe utilizzare Estrai Metodo . In altre parole, estrarre il codice in un metodo che si sarebbe poi chiamata.

Non vorrei provare a comprimere molto di più, le fodere uno sembrano più difficili da leggere rispetto alla versione verbose. E se si utilizza il metodo di estrazione, si tratta di un uno di linea;)

Uso della e-o trucco:

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

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

Probabilmente non è la soluzione più veloce, ma nessuno di cui questa opzione:

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

se get_list() può tornare None è possibile utilizzare:

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

Vantaggi:

-una linea di

-basta chiamare get_list() una volta

-facile da capire

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

Personalmente ho trovato la prova e salvo più pulito stile di 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
if mylist != []:

       print(mylist[0])

   else:

       print(None)

Diverse persone hanno suggerito di fare qualcosa di simile:

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

Questo funziona in molti casi, ma funziona solo se la lista [0] non è uguale a 0, False, o una stringa vuota. Se la lista [0] è 0, False, o una stringa vuota, il metodo restituirà in modo non corretto Nessuno.

  
    

Ho creato questo bug nel mio codice una volta di troppo!

  

Non è il pitone idiomatica equivalente agli operatori ternari in stile C

cond and true_expr or false_expr

es.

list = get_list()
return list and list[0] or None
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top