Dlaczego w Pythonie nie ma pierwszej(iterowalnej) wbudowanej funkcji?

Zastanawiam się, czy nie ma powodu, dla którego nie ma first(iterable) we wbudowanych funkcjach Pythona, nieco podobnych do any(iterable) i all(iterable) (może być schowany gdzieś w module stdlib, ale nie widzę go w itertools). first dokonałby oceny generatora zwarciowego, aby uniknąć niepotrzebnych (i potencjalnie nieskończonej liczby) operacji, np.

def identity(item):
    return item

def first(iterable, predicate=identity):
    for item in iterable:
        if predicate(item):
            return item
    raise ValueError('No satisfactory value found')

W ten sposób możesz wyrazić rzeczy takie jak:

denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
    if all(i % denominators == 0 for denominator in denominators))

Najwyraźniej nie można zrobić list(generator)[0] W takim przypadku, ponieważ generator się nie wyłącza.

Lub jeśli masz kilka wyrażeń regularnych do dopasowania (przydatne, gdy wszystkie mają ten sam interfejs groupdict):

match = first(regex.match(big_text) for regex in regexes)

Oszczędzasz dużo niepotrzebnego przetwarzania, unikając list(generator)[0] i zwarcia przy pozytywnym dopasowaniu.

Author: cdleary, 2009-07-03

7 answers

Jeśli masz iterator, możesz po prostu wywołać jego metodę next. Coś w stylu:

In [3]: (5*x for x in xrange(2,4)).next()
Out[3]: 10
 46
Author: liori,
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-16 17:54:56

Istnieje Pakiet Pypi o nazwie"first" , który robi to:

>>> from first import first
>>> first([0, None, False, [], (), 42])
42

Oto jak można użyć, aby zwrócić pierwszą nieparzystą liczbę, na przykład:

>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1)
7

Jeśli chcesz tylko zwrócić pierwszy element z iteratora niezależnie od tego, czy jest prawdziwy, czy nie, zrób to:

>>> first([0, None, False, [], (), 42], key=lambda x: True)
0

Jest to bardzo mały pakiet: zawiera tylko tę funkcję, nie ma zależności i działa na Pythonie 2 i 3. Jest to pojedynczy plik, więc nie musisz go nawet instalować, aby używać to.

W rzeczywistości, oto prawie cały kod źródłowy (z wersji 2.0.1, autorstwa Hynka Schlawacka, wydany na licencji MIT):

def first(iterable, default=None, key=None):
    if key is None:
        for el in iterable:
            if el:
                return el
    else:
        for el in iterable:
            if key(el):
                return el
    return default
 13
Author: Flimm,
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-01-07 12:02:02

Zadałem Ostatnio podobne pytanie (zostało już oznaczone jako DUPLIKAT tego pytania). Moim zmartwieniem było również to, że chciałbym użyć wbudowanych tylko, aby rozwiązać problem znalezienia pierwszej prawdziwej wartości generatora. Moje własne rozwiązanie było wtedy takie:

x = next((v for v in (f(x) for x in a) if v), False)

Dla przykładu znalezienia pierwszego dopasowania regexp(Nie pierwszego dopasowania!) to by wyglądało tak:

patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ]
text = 'abc'
firstMatch = next(
  (match for match in
    (re.match(pattern, text) for pattern in patterns)
   if match),
  False)

Nie ocenia predykatu dwukrotnie (jak trzeba by to zrobić, gdyby tylko wzór został zwrócony) i nie używa hacków jak miejscowi w składach.

Ale ma zagnieżdżone dwa generatory, gdzie logika nakazuje używać tylko jednego. Więc lepsze rozwiązanie byłoby miłe.

 10
Author: Alfe,
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-05-23 11:47:17

Jest pewna dwuznaczność w twoim pytaniu. Twoja definicja Pierwsza i przykład regex sugerują, że istnieje test logiczny. Ale przykład mianowników jawnie ma klauzulę if; więc jest tylko zbiegiem okoliczności, że każda liczba całkowita jest prawdziwa.

Wygląda jak połączenie next i itertools.ifilter da ci to, czego chcesz.

match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))
 6
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
2016-02-16 17:37:35

W itertools znajduje się iterator" slice". Emuluje operacje slice, które znamy z Pythona. To czego szukasz to coś podobnego do tego:

myList = [0,1,2,3,4,5]
firstValue = myList[:1]

Odpowiednik itertools dla iteratorów:

from itertools import islice
def MyGenFunc():
    for i in range(5):
        yield i

mygen = MyGenFunc()
firstValue = islice(mygen, 0, 1)
print firstValue 
 5
Author: Zoran Pavlovic,
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-09-03 10:30:54

Haskell wykorzystuje to, co właśnie opisałeś, jako funkcję take (lub jako funkcję częściową take 1, technicznie). Python Cookbook ma napisane Generatory-wrappery, które wykonują taką samą funkcjonalność jak take, takeWhile, i drop w Haskell.

Ale jeśli chodzi o to, dlaczego to nie jest wbudowane, zgadujesz tak samo dobrze jak ja.
 4
Author: Mark Rushakoff,
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
2009-07-03 00:22:29

Możesz użyć programu star unpacking, który jest obsługiwany w Pythonie 3.x. powinieneś przeczytać ten PEP: https://www.python.org/dev/peps/pep-3132/

x = [0, 1, 2, 3]
first, *rest = x
print(first)
print(rest)

Python iteruje nad listą i przypisuje każdy element do zmiennych po lewej stronie. Zmienne z gwiazdą przed, bierze resztę jako nową listę.

 0
Author: DeaD_EyE,
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-02-26 09:01:23