Idiom Pythona zwracający pierwszą pozycję lub brak

Jestem pewien, że jest prostszy sposób na zrobienie tego, co nie przychodzi mi do głowy.

Wywołuję kilka metod, które zwracają listę. Lista może być pusta. Jeśli lista nie jest pusta, chcę zwrócić pierwszy element; w przeciwnym razie chcę zwrócić żaden. Ten kod działa:
my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None
Wydaje mi się, że powinien istnieć prosty idiom jednolinijkowy, ale nie mogę o tym myśleć. Naprawdę?

Edit:

The reason that I ' m szukając tu wyrażenia jednowierszowego nie jest tak, że lubię niesamowicie zwięzły kod, ale dlatego, że muszę napisać dużo takiego kodu:

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

To, co chciałbym robić, z pewnością można osiągnąć za pomocą funkcji (i prawdopodobnie będzie):

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

Wysłałem pytanie, ponieważ często jestem zaskoczony tym, co proste wyrażenia w Pythonie mogą zrobić, i pomyślałem, że pisanie funkcji było Głupotą, jeśli istnieje proste wyrażenie może zrobić trik. Ale widząc te odpowiedzi, wydaje się, że funkcja jest prostym rozwiązaniem.

Author: Elias Zamaria, 2008-12-12

23 answers

Python 2.6 +

next(iter(your_list), None)

Jeśli your_list może być 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

Przykład:

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

Inną opcją jest wbudowanie powyższej funkcji:

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

Aby uniknąć break możesz napisać:

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

Gdzie:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return
 113
Author: jfs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-23 11:45:10

Najlepszy sposób jest taki:

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

Można też zrobić to w jednej linijce, ale programiście jest o wiele trudniej czytać:

return (get_list()[:1] or [None])[0]
 179
Author: efotinis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 20:12:27
(get_list() or [None])[0]
To powinno zadziałać.

BTW nie użyłem zmiennej list, ponieważ to nadpisuje wbudowaną funkcję list().

Edit: miałem nieco prostszą, ale błędną wersję tutaj wcześniej.

 55
Author: recursive,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-08 15:15:17

Najbardziej idiomatycznym sposobem Pythona jest użycie next () na iteratorze, ponieważ list jest iterable . tak jak @J. F. Sebastian dodał komentarz 13 grudnia 2011.

next(iter(the_list), None) zwraca None, Jeśli the_list jest puste. zobacz Next() Python 2.6+

Lub jeśli wiesz na pewno the_list nie jest pusty:

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

 30
Author: Devy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-20 06:36:29

Rozwiązanie OP jest prawie gotowe, jest tylko kilka rzeczy, które sprawią, że będzie bardziej Pythoniczne.

Po pierwsze, nie ma potrzeby, aby uzyskać długość listy. Puste listy w Pythonie oceniają na False w sprawdzeniu if. Po prostu powiedz

if list:

Dodatkowo, bardzo złym pomysłem jest przypisywanie zmiennych, które nakładają się na słowa zarezerwowane. "lista" jest słowem zastrzeżonym w Pythonie.

Więc zmieńmy to na

some_list = get_list()
if some_list:

Bardzo ważnym punktem, którego wiele rozwiązań tutaj brakuje, jest że wszystkie funkcje/metody Pythona domyślnie nie zwracają żadnej . Spróbuj wykonać poniższe czynności.

def does_nothing():
    pass

foo = does_nothing()
print foo

O ile nie musisz zwracać None, aby zakończyć funkcję wcześniej, nie jest konieczne zwracanie jawnie None. Dość zwięźle, po prostu zwróć pierwszy wpis, gdyby istniał.

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

I wreszcie, być może to było dorozumiane, ale żeby było jawne (ponieważ explicit jest lepszy niż implicit ), nie powinieneś mieć swojej funkcji get the list from another function; po prostu podaj go jako parametr. Tak więc końcowy wynik będzie

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

my_list = get_list()
first_item = get_first_item(my_list)

Jak mówiłem, operacja była prawie na miejscu, i tylko kilka akcentów nadaje mu smak pytona, którego szukasz.

 10
Author: gotgenes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 22:54:01

Jeśli próbujesz wyrwać pierwszą rzecz (lub żadnej) z listy, możesz przełączyć się na generator, aby zrobić to tak:

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

Pro: działa, jeśli blah nie jest indeksowalny Con: to nieznana składnia. Jest to przydatne podczas hakowania i filtrowania rzeczy w ipython.

 8
Author: Aidan Kane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-29 14:43:44
for item in get_list():
    return item
 3
Author: A. Coady,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 21:48:57

Szczerze mówiąc, nie sądzę, że istnieje lepszy idiom: twój jest jasny i zwięzły - nie ma potrzeby niczego "lepszego". Może, ale to naprawdę kwestia gustu, można by zmienić if len(list) > 0: na if list: - pusta lista zawsze będzie oceniana na False.

W powiązaniu, Python jest a nie Perlem (no pun intended!), nie musisz zdobywać jak najfajniejszego kodu.
Właściwie, najgorszy kod jaki widziałem w Pythonie, był również bardzo fajny : -) i całkowicie nie do utrzymania.

By sposób, w jaki większość rozwiązań, które tu widziałem, nie bierze pod uwagę, gdy list[0] zwraca wartość False (np. pusty łańcuch lub zero) - w tym przypadku wszystkie zwracają None I not the correct element.

 2
Author: Roberto Liffredo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 23:14:19

Do diabła z tym, oto jeszcze jedna możliwość.

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

Benefit: Ta metoda obsługuje przypadek, w którym get_list jest None, bez użycia try / except lub assignment. Z tego co wiem, żadna z powyższych implementacji nie poradzi sobie z tą możliwością

Upadki: funkcja get_list() jest wywoływana dwukrotnie, dość niepotrzebnie, szczególnie jeśli lista jest długa i / lub utworzona podczas wywoływania funkcji.

Prawda jest taka, że moim zdaniem bardziej" Pythoniczne " jest dostarczanie kodu, który jest czytelny niż zrobić jednolinijkowy tylko dlatego, że można:) muszę przyznać, że jestem winny wiele razy niepotrzebnie kompresji kodu Pythona tylko dlatego, że jestem pod wrażeniem, jak mały mogę sprawić, że złożona funkcja wygląda:)

Edytuj: Jak skomentował poniżej użytkownik "hasen j", powyższe wyrażenie warunkowe jest nowe w Pythonie 2.5, jak opisano tutaj: https://docs.python.org/whatsnew/2.5.html#pep-308 dzięki, hasen!

 2
Author: Chris Cameron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-11-14 03:23:52

Idiom Pythona zwracający pierwszą pozycję czy brak?

Najbardziej Pythoniczne podejście jest tym, co najbardziej upvoted odpowiedź zademonstrowała, i to była pierwsza rzecz, która przyszła mi do głowy, gdy czytałem pytanie. Oto jak go użyć, po pierwsze, jeśli ewentualnie pusta lista zostanie przekazana do funkcji: {]}

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

I jeśli lista jest zwracana z get_list funkcji:

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

Inne sposoby, aby to zrobić tutaj, z objaśnienia

for

Kiedy zacząłem myśleć o sprytnych sposobach, to jest druga rzecz, o której pomyślałem:]}
for item in get_list():
    return item

Zakłada, że funkcja kończy się tutaj, domyślnie zwracając None Jeśli get_list zwraca pustą listę. Poniższy jawny kod jest dokładnie równoważny:

for item in get_list():
    return item
return None

if some_list

Zaproponowano również następujące rozwiązanie (poprawiłem nieprawidłową nazwę zmiennej), które również używa implicit None. Byłoby to lepsze od powyższego, ponieważ używa logicznego sprawdzenia zamiast iteracji, która może się nie wydarzyć. Powinno to być łatwiejsze do natychmiastowego zrozumienia, co się dzieje. Ale jeśli piszemy dla czytelności i konserwacji, powinniśmy również dodać wyraziste return None na końcu:

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

Slice or [None] and select zeroth index

[[38]}ten jest również w najbardziej głosowanej odpowiedzi:
return (get_list()[:1] or [None])[0]

Plasterek jest niepotrzebny i tworzy dodatkową listę jednego elementu w pamięci. Poniżej powinno być więcej performant. Aby wyjaśnić, or zwraca drugi element, jeśli pierwszy jest False w kontekście logicznym, więc jeśli get_list zwróci pustą listę, wyrażenie zawarte w nawiasach zwróci listę z "None", do której będzie dostęp za pomocą indeksu 0:

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

Następny używa faktu, że i zwraca drugi element, jeśli pierwszy jest True W kontekście logicznym, a ponieważ odwołuje się do my_list dwa razy, nie jest lepszy od wyrażenia trójkowego (i technicznie nie jest to one-liner):

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

next

Następnie mamy następujące sprytne wykorzystanie wbudowanego next i iter

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

Aby wyjaśnić, iter zwraca iterator z metodą .next. (.__next__ w Pythonie 3.) Następnie wbudowana metoda next wywołuje tę metodę .next, a jeśli iterator jest wyczerpany, Zwraca wartość domyślną, którą podajemy, None.

Nadmiarowe wyrażenie ternary (a if b else c) i krążące wstecz

Poniżej zaproponowano, ale odwrotność byłaby korzystniejsza, ponieważ logika jest zwykle lepiej rozumiane w pozytywie zamiast negatywu. Ponieważ get_list jest wywoływane dwa razy, o ile wynik nie zostanie zapamiętany w jakiś sposób, będzie to działać słabo:

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

Lepsza odwrotność:

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

Jeszcze lepiej, Użyj zmiennej lokalnej tak, że {[12] } jest wywoływana tylko jeden raz, i masz zalecane rozwiązanie Pythonic najpierw omówione:

l = get_list()
return l[0] if l else None
 2
Author: Aaron Hall,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-14 15:37:39

Z ciekawości, sprawdziłem czas na dwóch rozwiązaniach. Rozwiązanie, które używa instrukcji return do przedwczesnego zakończenia pętli for, jest nieco droższe na moim komputerze z Pythonem 2.5.1, podejrzewam, że ma to związek z konfiguracją 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

To są czasy, które mam:

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.
 1
Author: gotgenes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-16 07:07:33

Jeśli chodzi o idiomy, istnieje itertools recipe o nazwie nth.

Z receptur itertools:

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

Jeśli chcesz jednolinijkowe, rozważ zainstalowanie biblioteki, która implementuje ten przepis dla Ciebie, np. more_itertools:

import more_itertools as mit

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

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

Dostępne jest inne narzędzie, które zwraca tylko pierwszy element o nazwie more_itertools.first.

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

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

Te narzędzia itertools skalują się ogólnie dla dowolnych iterowalnych, nie tylko dla list.

 1
Author: pylang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-25 17:15:13
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

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

BTW: przerobiłbym ogólny przepływ programu na coś takiego:

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

(unikanie powtarzania w miarę możliwości)

 0
Author: ttepasse,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-16 13:23:21

A może tak:

(my_list and my_list[0]) or None

Notatka: powinno to działać poprawnie dla list obiektów, ale może zwrócić niepoprawną odpowiedź w przypadku listy liczb lub łańcuchów w komentarzach poniżej.

 0
Author: VitalyB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-13 15:17:42

Możesz użyć metody ekstrakcji . Innymi słowy wyodrębnij ten kod do metody, którą następnie wywołasz.

Nie próbowałbym go bardziej kompresować, Jedynka wydaje się trudniejsza do odczytania niż wersja słowna. A jeśli używasz metody Extract, to jest to jeden liner;)

 -1
Author: Stephane Grenier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 20:10:04
try:
    return a[0]
except IndexError:
    return None
 -1
Author: limscoder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 20:38:00

Użycie sztuczki and-or:

a = get_list()
return a and a[0] or None
 -1
Author: titaniumdecoy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-12 23:53:16

A co z: next(iter(get_list()), None)? Może nie jest tu najszybszy, ale jest standardowy (począwszy od Pythona 2.6) i zwięzły.

 -1
Author: Marcin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-27 17:21:42

Prawdopodobnie nie najszybsze rozwiązanie, ale nikt nie wspomniał o tej opcji:

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

Jeśli get_list() może zwrócić None możesz użyć:

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

Zalety:

- jedna linia

- wystarczy zadzwonić get_list() raz

-łatwe do zrozumienia

 -1
Author: Eric Marcos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-04 18:37:07

Mój przypadek użycia polegał tylko na ustawieniu wartości zmiennej lokalnej.

Osobiscie znalazlem try i except style cleaner do przeczytania

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

Niż krojenie listy.

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item
 -1
Author: bkowshik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-20 07:00:51
if mylist != []:

       print(mylist[0])

   else:

       print(None)
 -1
Author: PrabhuPrakash,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-25 10:13:22

Kilka osób zasugerowało zrobienie czegoś takiego:

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

To działa w wielu przypadkach, ale będzie działać tylko wtedy, gdy list[0] nie jest równe 0, False lub pusty łańcuch. Jeśli list[0] ma wartość 0, False lub pusty łańcuch, metoda niepoprawnie zwróci None.

Stworzyłem ten błąd we własnym kodzie o jeden raz za dużo !

 -1
Author: Clint Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-08 12:49:26

Czy idiomatyczny python nie jest odpowiednikiem trójnawowych operatorów w stylu C?]}

cond and true_expr or false_expr

Ie.

list = get_list()
return list and list[0] or None
 -2
Author: Jimmy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-15 16:20:43