2013-03-13 9 views
45

Zakładając, że mam listę z ogromną liczbą przedmiotów.python: pobierz numer pozycji z listy (sekwencja) z pewnym warunkiem

l = [ 1, 4, 6, 30, 2, ... ] 

Chcę uzyskać liczbę elementów z tej listy, gdzie element powinien spełniać pewne warunki. Moją pierwszą myślą było:

count = len([i for i in l if my_condition(l)]) 

Ale jeśli my_condition() filtruje lista ma również dużą liczbę elementów, myślę, że tworząc nową listę dla przefiltrowanej wyniku jest tylko marnowanie pamięci. Dla efektywności, IMHO, powyżej połączenie nie może być lepiej niż:

count = 0 
for i in l: 
    if my_condition(l): 
     count += 1 

Czy jest jakiś sposób funkcjonalny styl osiągnąć, aby uzyskać # elementów, które spełniają określone warunki, nie generując tymczasową listę?

Z góry dziękuję.

+3

Wybór między generatorami i listami jest wyborem czasu wykonania i zużycia pamięci. Byłbyś zaskoczony, jak często wyniki są sprzeczne z intuicją, jeśli profilujesz kod. Przedwczesna optymalizacja jest źródłem wszelkiego zła. –

Odpowiedz

12

Chcesz raczej generator comprehension niż listę tutaj.

Na przykład

l = [1, 4, 6, 7, 30, 2] 

def my_condition(x): 
    return x > 5 and x < 20 

print sum(1 for x in l if my_condition(x)) 
# -> 2 
print sum(1 for x in range(1000000) if my_condition(x)) 
# -> 14 

Albo użyć itertools.imap (chociaż myślę, że jawne i generatory lista wyrażeń wyglądać nieco więcej pythonic).

Należy zauważyć, że chociaż nie jest to oczywiste z przykładu sum, można ładnie komponować zgadywanie generatora. Na przykład,

inputs = xrange(1000000)  # In Python 3 and above, use range instead of xrange 
odds = (x for x in inputs if x % 2) # Pick odd numbers 
sq_inc = (x**2 + 1 for x in odds) # Square and add one 
print sum(x/2 for x in sq_inc)  # Actually evaluate each one 
# -> 83333333333500000 

Świetną rzeczą jest to, że tej techniki można określić koncepcyjnie odrębne etapy w kodzie bez wymuszania oceny i przechowywanie w pamięci, aż wynik końcowy jest oceniany.

66

Można użyć generator expression:

>>> l = [1, 3, 7, 2, 6, 8, 10] 
>>> sum(1 for i in l if i % 4 == 3) 
2 

lub nawet

>>> sum(i % 4 == 3 for i in l) 
2 

który wykorzystuje fakt, że int(True) == 1.

Alternatywnie, można użyć itertools.imap (Python 2) lub po prostu map (Python 3):

>>> def my_condition(x): 
...  return x % 4 == 3 
... 
>>> sum(map(my_condition, l)) 
2 
+4

Co więcej, używa faktu, że 'True + True == 2' – mgilson

+1

@mgilson: Nie sądzę, że kiedykolwiek to zrobi -" start "domyślnie wynosi 0, więc pierwszym dodatkiem jest" True + 0', nie? – DSM

+4

Tak. Może powinienem być bardziej jasny ... Nie ma znaczenia, czym jest 'int (True)'. 'int (" 1 ") == 1' również, ale to nie znaczy, że możesz zrobić" "1" + 0 ". Liczy się to, jak python ocenia "integer + True" lub "integer + False". – mgilson

1
from itertools import imap 
sum(imap(my_condition, l)) 
3

mógłby zrobić coś takiego:

l = [1,2,3,4,5,..] 
count = sum(1 for i in l if my_condition(i)) 

który tylko dodaje 1 dla każdego elementu, który spełnia warunek.

4

Można to również zrobić za pomocą reduce jeśli wolisz Programowanie funkcyjne

reduce(lambda count, i: count + my_condition(i), l, 0) 

W ten sposób można zrobić tylko 1 przepustkę i nie lista pośrednia jest generowany.

Powiązane problemy