Rozważmy następujący kod python2
In [5]: points = [ (1,2), (2,3)]
In [6]: min(points, key=lambda (x, y): (x*x + y*y))
Out[6]: (1, 2)
Nie jest to obsługiwane w python3 i muszę wykonać następujące czynności:
>>> min(points, key=lambda p: p[0]*p[0] + p[1]*p[1])
(1, 2)
To jest bardzo brzydkie. Gdyby lambda była funkcją, mógłbym to zrobić
def some_name_to_think_of(p):
x, y = p
return x*x + y*y
Usunięcie tej funkcji w pythonie3 wymusza na kodzie brzydkie postępowanie (z magicznymi indeksami) lub tworzenie niepotrzebnych funkcji (najbardziej kłopotliwe jest wymyślenie dobrych nazw dla tych niepotrzebnych funkcji)
Myślę, że ta funkcja powinna zostać dodana przynajmniej do samych lambd. Czy jest dobra alternatywa?
Aktualizacja: używam następującego pomocnika rozszerzającego pomysł w odpowiedzi
def star(f):
return lambda args: f(*args)
min(points, key=star(lambda x,y: (x*x + y*y))
Update2: czystsza wersja dlastar
import functools
def star(f):
@functools.wraps(f):
def f_inner(args):
return f(*args)
return f_inner
python
python-3.x
balki
źródło
źródło
lambda
jest całkowite usunięcie z języka, a następnie cofnięcie zmian, które utrudniły korzystanie z niego, ale możesz spróbować opublikować posty na temat Pythona, jeśli chcesz wyrazić chęć ponownego dodania funkcji.lambda
w tym samym duchu, co onmap
,reduce
ifilter
.lambda
został przeznaczony do usunięcia w py3k, ponieważ jest to w zasadzie plaga języka. Ale nikt nie mógł zgodzić się na właściwą alternatywę dla definiowania anonimowych funkcji, więc w końcu Guido rzucił w górę ramiona w porażce i to wszystko.map
ifilter
są najlepiej zastąpione listowe, lubięreduce
)Odpowiedzi:
Nie, nie ma innego wyjścia. Zakryłaś to wszystko. Najlepszym sposobem byłoby poruszenie tego problemu na liście dyskusyjnej pomysłów na Python , ale bądź przygotowany na wiele dyskusji, aby zyskać trochę przyczepności.
Właściwie, żeby nie powiedzieć „nie ma wyjścia”, trzecim sposobem mogłoby być zaimplementowanie jeszcze jednego poziomu wywołań lambda tylko w celu rozwinięcia parametrów - ale byłoby to jednocześnie mniej wydajne i trudniejsze do odczytania niż twoje dwie sugestie:
min(points, key=lambda p: (lambda x,y: (x*x + y*y))(*p))
zaktualizuj Python 3.8
Obecnie dostępny jest język Python 3.8 alpha1, a wyrażenia przypisania PEP 572 są zaimplementowane.
Tak więc, jeśli ktoś używa sztuczki do wykonania wielu wyrażeń wewnątrz lambdy - zwykle robię to, tworząc krotkę i po prostu zwracając jej ostatni składnik, można to zrobić:
>>> a = lambda p:(x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1] >>> a((3,4)) 25
Należy pamiętać, że ten rodzaj kodu rzadko będzie bardziej czytelny lub praktyczny niż posiadający pełną funkcję. Nadal istnieją możliwe zastosowania - jeśli istnieją różne
point
jednolinijkowe, które by na tym działały , warto byłoby mieć namedtuple i użyć wyrażenia przypisania, aby skutecznie „rzutować” przychodzącą sekwencję na nazwaną tukę:>>> from collections import namedtuple >>> point = namedtuple("point", "x y") >>> b = lambda s: (p:=point(*s), p.x ** 2 + p.y ** 2)[-1]
źródło
def
której mowa w pytaniu pod nazwąsome_name_to_think-of
.key = lambda p: (x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
Zgodnie z http://www.python.org/dev/peps/pep-3113/ rozpakowywanie krotek zniknęło i
2to3
przetłumaczy je w następujący sposób:Co jest dość podobne do twojej implementacji.
źródło
Nie znam żadnych dobrych ogólnych alternatyw dla zachowania podczas rozpakowywania argumentów Pythona 2. Oto kilka sugestii, które mogą być przydatne w niektórych przypadkach:
jeśli nie możesz wymyślić imienia; użyj nazwy parametru słowa kluczowego:
def key(p): # more specific name would be better x, y = p return x**2 + y**3 result = min(points, key=key)
możesz zobaczyć, czy
namedtuple
poprawi czytelność kodu, jeśli lista jest używana w wielu miejscach:from collections import namedtuple from itertools import starmap points = [ (1,2), (2,3)] Point = namedtuple('Point', 'x y') points = list(starmap(Point, points)) result = min(points, key=lambda p: p.x**2 + p.y**3)
źródło
lambda (x, y):
ilambda x, y:
nie jest oczywista na pierwszy rzut oka.Chociaż argumenty destrukturyzujące zostały usunięte w Pythonie3, nie zostały one usunięte ze zrozumień. Można go nadużywać w celu uzyskania podobnego zachowania w Pythonie 3. W istocie korzystamy z faktu, że współprogramy pozwalają nam odwrócić funkcje na lewą stronę, a yield nie jest instrukcją, a zatem jest dozwolony w lambdach.
Na przykład:
points = [(1,2), (2,3)] print(min(points, key=lambda y: next(x*x + y*y for x,y in (lambda a: (yield a))(y))))
W porównaniu z akceptowaną odpowiedzią używania wrappera, rozwiązanie to jest w stanie całkowicie zniszczyć argumenty, podczas gdy wrapper niszczy tylko pierwszy poziom. To jest,
values = [(('A',1),'a'), (('B',0),'b')] print(min(values, key=lambda y: next(b for (a,b),c in (lambda x: (yield x))(y))))
W stosunku do
values = [(('A',1),'a'), (('B',0),'b')] print(min(points, key=lambda p: (lambda a,b: (lambda x,y: (y))(*a))(*p)))
Alternatywnie można to zrobić
values = [(('A',1),'a'), (('B',0),'b')] print(min(points, key=lambda y: next(b for (a,b),c in [y])))
Albo trochę lepiej
print(min(values, key=lambda y: next(b for ((a,b),c) in (y,))))
Ma to tylko zasugerować, że można to zrobić i nie należy tego traktować jako zalecenia.
źródło
Myślę, że im lepsza składnia
x * x + y * y let x, y = point
,let
słowo kluczowe powinno być starannie dobrane.Najbliższą wersją jest podwójna lambda.
lambda point: (lambda x, y: x * x + y * y)(*point)
Pomocnik funkcji wysokiego rzędu przydałby się na wypadek, gdybyśmy nadali jej właściwą nazwę.
def destruct_tuple(f): return lambda args: f(*args) destruct_tuple(lambda x, y: x * x + y * y)
źródło
Opierając się na sugestii Cuadue i komentarzu na temat rozpakowywania, który nadal jest obecny w zrozumieniu, możesz użyć, używając
numpy.argmin
:result = points[numpy.argmin(x*x + y*y for x, y in points)]
źródło
Inną opcją jest zapisanie go w generatorze tworzącym krotkę, w której klucz jest pierwszym elementem. Krotki są porównywane od początku do końca, więc zwracana jest krotka z najmniejszym pierwszym elementem. Następnie możesz zindeksować wynik, aby uzyskać wartość.
min((x * x + y * y, (x, y)) for x, y in points)[1]
źródło
Zastanów się, czy w pierwszej kolejności musisz rozpakować krotkę:
min(points, key=lambda p: sum(x**2 for x in p))
lub czy musisz podać wyraźne nazwy podczas rozpakowywania:
min(points, key=lambda p: abs(complex(*p))
źródło