Mam listę obiektów i chcę usunąć wszystkie obiekty, które są puste, z wyjątkiem jednego, używając filter
i lambda
wyrażenia.
Na przykład, jeśli dane wejściowe to:
[Object(name=""), Object(name="fake_name"), Object(name="")]
... wtedy wynik powinien wyglądać następująco:
[Object(name=""), Object(name="fake_name")]
Czy istnieje sposób na dodanie przypisania do lambda
wyrażenia? Na przykład:
flag = True
input = [Object(name=""), Object(name="fake_name"), Object(name="")]
output = filter(
(lambda o: [flag or bool(o.name), flag = flag and bool(o.name)][0]),
input
)
Odpowiedzi:
Operator wyrażenia przypisania
:=
dodany w Pythonie 3.8 obsługuje przypisanie wewnątrz wyrażeń lambda. Ten operator może występować tylko w wyrażeniu w nawiasach(...)
, w nawiasach[...]
lub w nawiasach{...}
ze względów składniowych. Na przykład będziemy mogli napisać:W Pythonie 2 możliwe było wykonywanie lokalnych przypisań jako efekt uboczny składanych list.
Jednak nie jest możliwe użycie żadnego z tych w twoim przykładzie, ponieważ twoja zmienna
flag
znajduje się w zakresie zewnętrznym, a nielambda
zasięgu. Nie ma to nic wspólnegolambda
, jest to ogólne zachowanie w Pythonie 2. Python 3 pozwala ominąć ten problem za pomocąnonlocal
słowa kluczowego wewnątrzdef
s, alenonlocal
nie można go używać wewnątrzlambda
s.Istnieje obejście (patrz poniżej), ale skoro już jesteśmy w temacie ...
W niektórych przypadkach możesz tego użyć, aby zrobić wszystko w
lambda
:Proszę nie.
... wracając do pierwotnego przykładu: chociaż nie możesz wykonywać przypisań do
flag
zmiennej w zakresie zewnętrznym, możesz użyć funkcji do zmodyfikowania poprzednio przypisanej wartości.Na przykład
flag
może to być obiekt, którego.value
ustawiamy za pomocąsetattr
:Gdybyśmy chcieli dopasować powyższy motyw, moglibyśmy użyć rozumienia list zamiast
setattr
:Ale tak naprawdę w poważnym kodzie zawsze powinieneś używać zwykłej definicji funkcji zamiast a,
lambda
jeśli zamierzasz wykonywać zewnętrzne przypisanie.źródło
.setattr()
i alikes ( słowniki też powinny robić np.) do włamania efektów ubocznych do kodu funkcjonalnego, pokazany został fajny kod @JeremyBanks :)assignment operator
!Nie możesz naprawdę utrzymywać stanu w wyrażeniu
filter
/lambda
(chyba że nadużywasz globalnej przestrzeni nazw). Możesz jednak osiągnąć coś podobnego, używając skumulowanego wyniku przekazywanego wreduce()
wyrażeniu:Możesz oczywiście nieco poprawić stan. W tym przypadku odfiltrowuje duplikaty, ale możesz również użyć
a.count("")
, na przykład, aby ograniczyć tylko puste ciągi.Nie trzeba dodawać, że możesz to zrobić, ale naprawdę nie powinieneś. :)
Wreszcie możesz zrobić wszystko w czystym Pythonie
lambda
: http://vanderwijk.info/blog/pure-lambda-calculus-python/źródło
Nie ma potrzeby używania lambdy, kiedy możesz usunąć wszystkie puste i wstawić z powrotem, jeśli zmieni się rozmiar wejściowy:
źródło
output = [x for x in input if x.name]
.Normalne przypisanie (
=
) nie jest możliwe wewnątrzlambda
wyrażenia, chociaż można wykonywać różne sztuczki zsetattr
przyjaciółmi i znajomymi.Jednak rozwiązanie problemu jest w rzeczywistości dość proste:
który ci da
Jak widać, zachowuje pierwszą pustą instancję zamiast ostatniej. Jeśli zamiast tego potrzebujesz ostatniej, odwróć listę przechodzącą do
filter
i odwróć listę wychodzącą zfilter
:który ci da
Jedna rzecz, o której należy pamiętać: aby to działało z dowolnymi obiektami, obiekty te muszą poprawnie implementować
__eq__
i__hash__
jak wyjaśniono tutaj .źródło
AKTUALIZACJA :
lub używając
filter
ilambda
:Poprzednia odpowiedź
OK, czy utkniesz na używaniu filtra i lambdy?
Wygląda na to, że lepiej byłoby to zrobić ze zrozumieniem słownikowym,
Myślę, że powód, dla którego Python nie zezwala na przypisanie w lambdzie, jest podobny do tego, dlaczego nie pozwala na przypisanie w zrozumieniu i ma to coś wspólnego z faktem, że te rzeczy są oceniane z
C
boku, a zatem mogą dać nam wzrost prędkości. Przynajmniej takie mam wrażenie po przeczytaniu jednego z esejów Guido .Domyślam się, że byłoby to również sprzeczne z filozofią posiadania jednego właściwego sposobu robienia dowolnej rzeczy w Pythonie.
źródło
TL; DR: Korzystając z idiomów funkcjonalnych, lepiej jest pisać kod funkcjonalny
Jak wiele osób zauważyło, w Pythonie przypisywanie lambd jest niedozwolone. Ogólnie rzecz biorąc, używając idiomów funkcjonalnych, lepiej jest myśleć w sposób funkcjonalny, co oznacza, że wszędzie tam, gdzie to możliwe, nie ma żadnych skutków ubocznych i żadnych przypisań.
Oto funkcjonalne rozwiązanie wykorzystujące lambdę. Przypisałem lambdę
fn
dla jasności (i dlatego, że jest trochę za długa).Możesz także zrobić to z iteratorami zamiast list, zmieniając trochę rzeczy. Masz również kilka różnych importów.
Zawsze możesz zmienić kod w celu zmniejszenia długości instrukcji.
źródło
Jeśli zamiast tego
flag = True
możemy wykonać import, myślę, że spełnia to kryteria:A może filtr lepiej napisać jako:
Lub po prostu dla prostej wartości logicznej, bez importu:
źródło
Pythonowym sposobem śledzenia stanu podczas iteracji są generatory. Metoda itertools jest dość trudna do zrozumienia IMHO, a próba hakowania lambd, aby to zrobić, jest po prostu głupia. Spróbuję:
Ogólnie rzecz biorąc, czytelność za każdym razem przewyższa zwartość.
źródło
Nie, nie możesz umieścić przypisania wewnątrz lambdy z powodu jej własnej definicji. Jeśli pracujesz z programowaniem funkcjonalnym, musisz założyć, że twoje wartości nie są zmienne.
Jednym z rozwiązań byłby następujący kod:
źródło
Jeśli potrzebujesz lambdy do zapamiętania stanu między wywołaniami, poleciłbym albo funkcję zadeklarowaną w lokalnej przestrzeni nazw, albo klasę z przeciążoną
__call__
. Teraz, gdy wszystkie moje ostrzeżenia dotyczące tego, co próbujesz zrobić, są już na uboczu, możemy uzyskać rzeczywistą odpowiedź na Twoje pytanie.Jeśli naprawdę potrzebujesz mieć swoją lambdę, aby mieć trochę pamięci między wywołaniami, możesz to zdefiniować w następujący sposób:
Następnie wystarczy przejść
f
dofilter()
. Jeśli naprawdę potrzebujesz, możesz odzyskać wartość,flag
wykonując następujące czynności:Alternatywnie możesz zmodyfikować globalną przestrzeń nazw, modyfikując wynik
globals()
. Niestety, nie można modyfikować lokalnej przestrzeni nazw w taki sam sposób, jak modyfikowanie wynikulocals()
nie wpływa na lokalną przestrzeń nazw.źródło
(let ((var 42)) (lambda () (setf var 43)))
.Możesz użyć funkcji bind, aby użyć pseudo-wyrażenia lambda. Następnie możesz użyć klasy opakowania dla flagi, aby umożliwić przypisanie.
źródło
Trochę niechlujne obejście, ale przypisanie w lambdach i tak jest nielegalne, więc nie ma to większego znaczenia. Możesz użyć wbudowanego
exec()
funkcji aby uruchomić przypisanie z wnętrza lambda, tak jak w tym przykładzie:źródło
Po pierwsze, nie musisz używać lokalnego przydziału do swojej pracy, po prostu sprawdź powyższą odpowiedź
po drugie, w prosty sposób można użyć locals () i globals () do pobrania tabeli zmiennych, a następnie do zmiany wartości
sprawdź ten przykładowy kod:
jeśli trzeba zmienić dodać zmienną globalną do Environ, spróbuj zastąpić mieszkańców () z globalnych ()
lista pythona comp jest fajna, ale większość projektów triditional nie akceptuje tego (jak flask: [)
mam nadzieję, że to pomoże
źródło
locals()
, w dokumentacji jest wyraźnie napisane, że jego zmiana w rzeczywistości nie zmienia zakresu lokalnego (a przynajmniej nie zawsze).globals()
z drugiej strony działa zgodnie z oczekiwaniami.globals()
. pastebin.com/5Bjz1mR4 (testowane zarówno w 2.6, jak i 3.2) to potwierdza.