Zdarzyło mi się, że mam podstawową potrzebę filtrowania: mam listę i muszę ją filtrować według atrybutu elementów.
Mój kod wyglądał tak:
my_list = [x for x in my_list if x.attribute == value]
Ale potem pomyślałem, czy nie byłoby lepiej tak napisać?
my_list = filter(lambda x: x.attribute == value, my_list)
Jest bardziej czytelny, a w razie potrzeby można wyjąć lambda, aby coś zyskać.
Pytanie brzmi: czy są jakieś zastrzeżenia dotyczące korzystania z drugiego sposobu? Jakaś różnica w wydajności? Czy całkowicie brakuje mi Pythonic Way ™ i czy powinienem to zrobić w jeszcze inny sposób (np. Używając itemgetter zamiast lambda)?
filter
było to bardziej czytelne. Kiedy masz proste wyrażenie, które mogą być używane jak jest w listcomp, ale musi być zapakowane w lambda (lub podobnie zbudowane zpartial
luboperator
funkcji, etc.), aby przekazaćfilter
, że właśnie wtedy listcomps wygrać.filter
jest obiekt generatora filtrów, a nie lista.Odpowiedzi:
Dziwne, jak różne jest piękno u różnych ludzi. Uważam, że lista jest znacznie bardziej zrozumiała niż
filter
+lambda
, ale używaj tego, co uznasz za łatwiejsze.Są dwie rzeczy, które mogą spowolnić twoje użycie
filter
.Pierwszym z nich jest narzut wywołania funkcji: jak tylko użyjesz funkcji Python (utworzonej przez
def
lublambda
), prawdopodobnie filtr będzie wolniejszy niż przetwarzanie listy. Prawie na pewno nie wystarczy, aby mieć znaczenie, i nie powinieneś dużo myśleć o wydajności, dopóki nie zorientujesz się w swoim kodzie i nie okaże się, że to wąskie gardło, ale będzie różnica.Innym narzutem, który może mieć zastosowanie, jest to, że lambda jest zmuszona uzyskać dostęp do zmiennej o zasięgu (
value
). Jest to wolniejsze niż uzyskiwanie dostępu do zmiennej lokalnej, a w Pythonie 2.x analiza listy ma dostęp tylko do zmiennych lokalnych. Jeśli używasz języka Python 3.x, lista jest uruchamiana w osobnej funkcji, więc dostęp do niej będzie również uzyskiwanyvalue
poprzez zamknięcie i ta różnica nie będzie miała zastosowania.Inną opcją do rozważenia jest użycie generatora zamiast zrozumienia listy:
Następnie w swoim głównym kodzie (gdzie liczy się czytelność) zastąpiłeś zarówno rozumienie listy, jak i filtr, miejmy nadzieję, znaczącą nazwę funkcji.
źródło
[]
na()
. Zgadzam się również, że kompilacja listy jest piękniejsza.filter
się, że jesteś szybszy przy użyciu funkcji zwrotnej Python.Jest to dość religijna kwestia w Pythonie. Mimo że Guido rozważał usunięcie
map
,filter
awreduce
Pythonie 3 było dość luzu, który ostateczniereduce
został przeniesiony tylko z wbudowanych do funools.reduce .Osobiście uważam, że listy są łatwiejsze do odczytania. Bardziej wyraźne jest to, co dzieje się z wyrażeniem,
[i for i in list if i.attribute == value]
ponieważ całe zachowanie jest na powierzchni, a nie wewnątrz funkcji filtra.Nie martwiłbym się zbytnio różnicą wydajności między tymi dwoma podejściami, ponieważ jest ona marginalna. Naprawdę zoptymalizowałbym to tylko, gdyby okazało się, że jest to wąskie gardło w twojej aplikacji, co jest mało prawdopodobne.
Również dlatego, że BDFL chciał
filter
odejść od języka, to z pewnością automatycznie czyni listy ze zrozumiałymi pythonami ;-)źródło
Ponieważ jakakolwiek różnica prędkości musi być niewielka, to czy użyć filtrów, czy też list, sprowadza się to do gustu. Ogólnie rzecz biorąc, jestem skłonny używać rozumienia (co wydaje się zgadzać z większością innych odpowiedzi tutaj), ale jest jeden przypadek, w którym wolę
filter
.Bardzo częstym przypadkiem użycia jest wyciągnięcie wartości jakiegoś iterowalnego X z zastrzeżeniem predykatu P (x):
ale czasami chcesz najpierw zastosować jakąś funkcję do wartości:
Jako konkretny przykład rozważmy
Myślę, że wygląda to nieco lepiej niż używanie
filter
. Ale teraz zastanów sięW tym przypadku chcemy
filter
przeciw obliczonej wartości obliczonej. Oprócz kwestii dwukrotnego obliczenia kostki (wyobraź sobie droższe obliczenia), istnieje także kwestia dwukrotnego napisania wyrażenia, co narusza estetykę SUCHEGO . W takim przypadku byłbym skłonny użyćźródło
[prime(i) for i in [x**3 for x in range(1000)]]
x*x*x
nie może być liczbą pierwszą, ponieważ tak jestx^2
ix
tak naprawdę, przykład ten nie ma sensu w sensie matematycznym, ale może nadal jest pomocny. (Może moglibyśmy znaleźć coś lepszego?)prime_cubes = filter(prime, (x*x*x for x in range(1000)))
prime_cubes = [1]
zaoszczędzić zarówno pamięć, jak i cykle procesora ;-)[]
Chociaż
filter
może to być „szybszy sposób”, „pytonicznym sposobem” byłoby nie przejmowanie się takimi rzeczami, chyba że wydajność jest absolutnie krytyczna (w takim przypadku nie używałbyś Pythona!).źródło
Pomyślałem, że po prostu dodam, że w Pythonie 3 filter () jest w rzeczywistości obiektem iteratora, więc musisz zbudować filtrowaną listę do wywołania metody filter do list (). Tak więc w python 2:
listy b i c mają te same wartości i zostały uzupełnione mniej więcej w tym samym czasie, gdy filter () był równoważny [x dla x in y, jeśli z]. Jednak w 3 ten sam kod pozostawiłby listę c zawierającą obiekt filtrujący, a nie filtrowaną listę. Aby wygenerować te same wartości w 3:
Problem polega na tym, że list () przyjmuje iterowalny argument i tworzy nową listę z tego argumentu. Powoduje to, że użycie filtru w ten sposób w Pythonie 3 zajmuje nawet dwukrotnie więcej czasu niż metoda [x for x in y if z], ponieważ musisz iterować dane wyjściowe z filter (), a także oryginalną listę.
źródło
Istotną różnicą jest to, że zrozumienie listy zwróci
list
chwilę, podczas gdy filtr zwróci afilter
, którego nie można manipulować jaklist
(tj. Wywołanielen
go, które nie działa z powrotemfilter
).Mój własny proces uczenia się doprowadził mnie do podobnego problemu.
Biorąc to pod uwagę, jeśli istnieje sposób, aby uzyskać wynik
list
zfilter
, trochę tak jak zrobiłbyś w .NET, kiedy to zrobiszlst.Where(i => i.something()).ToList()
, jestem ciekawy, aby to wiedzieć.EDYCJA: Tak jest w przypadku Pythona 3, a nie 2 (patrz dyskusja w komentarzach).
źródło
a = [1, 2, 3, 4, 5, 6, 7, 8]
f = filter(lambda x: x % 2 == 0, a)
lc = [i for i in a if i % 2 == 0]
>>> type(f)
<class 'filter'>
>>> type(lc)
<class 'list'>
list()
na wynik:list(filter(my_func, my_iterable))
. I oczywiście można zastąpićlist
zset
, lubtuple
, lub cokolwiek innego, która pobiera iterable. Ale dla kogokolwiek innego niż programistów funkcjonalnych, sprawa jest jeszcze silniejsza, aby użyć zrozumienia listy, a niefilter
wyraźnej konwersji nalist
.Drugi sposób uważam za bardziej czytelny. Mówi dokładnie, jaki jest zamiar: przefiltruj listę.
PS: nie używaj „listy” jako nazwy zmiennej
źródło
ogólnie
filter
jest nieco szybszy, jeśli używasz wbudowanej funkcji.Spodziewałbym się, że lista będzie nieco szybsza w twoim przypadku
źródło
Filtr jest właśnie taki. Filtruje elementy listy. Możesz zobaczyć, że definicja wspomina to samo (w oficjalnym linku do dokumentów, o którym wspomniałem wcześniej). Podczas gdy rozumienie listy jest czymś, co tworzy nową listę po działaniu na coś z poprzedniej listy (zarówno filtrowanie, jak i rozumienie listy tworzy nową listę i nie wykonuje operacji zamiast starszej listy. Nowa lista tutaj jest czymś w rodzaju listy z , powiedzmy, zupełnie nowy typ danych. Jak konwersja liczb całkowitych na ciąg itp.)
W twoim przykładzie lepiej jest używać filtrowania niż listowania, zgodnie z definicją. Jeśli jednak chcesz, powiedzmy inny atrybut z elementów listy, w twoim przykładzie ma zostać pobrana jako nowa lista, możesz użyć rozumienia listy.
Tak właśnie pamiętam o analizie filtrów i list. Usuń kilka rzeczy z listy i pozostań nienaruszonych, użyj filtru. Skorzystaj z własnej logiki przy elementach i stwórz rozwodnioną listę odpowiednią do określonego celu, skorzystaj ze zrozumienia listy.
źródło
Oto krótki fragment, którego używam, gdy muszę odfiltrować coś po z listą. Po prostu kombinacja filtra, lambda i list (zwana także lojalnością kota i czystością psa).
W tym przypadku czytam plik, usuwam puste linie, komentuję linie i wszystko po komentarzu do linii:
źródło
file_contents = list(filter(None, (s.partition('#')[0].strip() for s in lines)))
Oprócz zaakceptowanej odpowiedzi istnieje przypadek narożny, w którym należy użyć filtru zamiast listy. Jeśli listy nie da się zhashować, nie można jej przetworzyć bezpośrednio ze zrozumieniem listy. Rzeczywistym przykładem jest sytuacja, gdy używasz
pyodbc
do odczytu wyników z bazy danych. TefetchAll()
wynikicursor
to lista unhashable. W tej sytuacji, aby bezpośrednio manipulować zwróconymi wynikami, należy użyć filtru:Jeśli użyjesz tutaj rozumienia listy, pojawi się błąd:
źródło
>>> hash(list()) # TypeError: unhashable type: 'list'
po drugie, to działa dobrze:processed_data = [s for s in data_from_db if 'abc' in s.field1 or s.StartTime >= start_date_time]
Zajęło mi trochę czasu, aby zapoznać się z
higher order functions
filter
imap
. Więc przyzwyczaiłem się do nich i naprawdę mi się podobało,filter
ponieważ było jasne, że filtruje, zachowując wszystko, co jest prawdą, i czułem się fajnie, że znałem niektórefunctional programming
warunki.Potem czytam ten fragment (Fluent Python Book):
A teraz myślę, po co zawracać sobie głowę koncepcją
filter
/map
jeśli możesz to osiągnąć dzięki już szeroko rozpowszechnionym idiomom, takim jak listy. Ponadtomaps
ifilters
są rodzajem funkcji. W tym przypadku wolę używaćAnonymous functions
lambda.Wreszcie, tylko ze względu na przetestowanie, określiłem obie metody (
map
ilistComp
) i nie zauważyłem żadnej istotnej różnicy prędkości, która uzasadniałaby argumentowanie na ten temat.źródło
Co ciekawe, w Pythonie 3 widzę, że filtr działa szybciej niż wyrażenia listowe.
Zawsze myślałem, że listy będą bardziej wydajne. Coś w stylu: [nazwa dla nazwy w brand_names_db, jeśli nazwa nie jest Brak] Wygenerowany kod bajtowy jest nieco lepszy.
Ale w rzeczywistości są wolniejsze:
źródło
if not None
w listowego ty są zdefiniowaniu funkcji lambda (zawiadomienieMAKE_FUNCTION
oświadczenie). Po drugie, wyniki są różne, ponieważ wersja ze zrozumieniem listy usunie tylkoNone
wartość, natomiast wersja filtru usunie wszystkie wartości „fałszowania”. Powiedziawszy to, cały cel znakowania mikrobiologicznego jest bezużyteczny. To milion powtórzeń, razy 1k przedmiotów! Różnica jest znikoma .Moje podanie
źródło
i
nigdy nie było powiedzianedict
, i nie ma takiej potrzebylimit
. Poza tym, czym to się różni od tego, co sugeruje PO i jak odpowiada na pytanie?