Подтвердить что ты не робот

Каков хороший эквивалент python3 для распаковки автоматической кортежей в лямбда?

Рассмотрим следующий код python2

In [5]: points = [ (1,2), (2,3)]

In [6]: min(points, key=lambda (x, y): (x*x + y*y))
Out[6]: (1, 2)

Это не поддерживается в python3, и я должен сделать следующее:

>>> min(points, key=lambda p: p[0]*p[0] + p[1]*p[1])
(1, 2)

Это очень уродливо. Если лямбда была функцией, я мог бы сделать

def some_name_to_think_of(p):
  x, y = p
  return x*x + y*y

Удаление этой функции в python3 заставляет код либо делать уродливый путь (с волшебными индексами), либо создавать ненужные функции (самой трудной частью является думать о хороших именах для этих ненужных функций)

Я думаю, что эта функция должна быть добавлена, по крайней мере, только к лямбдам. Есть ли хорошая альтернатива?


Обновление: я использую следующий помощник, расширяющий идею в ответе

def star(f):
  return lambda args: f(*args)

min(points, key=star(lambda x,y: (x*x + y*y))

Update2: более чистая версия для star

import functools

def star(f):
    @functools.wraps(f):
    def f_inner(args):
        return f(*args)
    return f_inner
4b9b3361

Ответ 1

Нет, другого пути нет. Вы покрыли все это. Чтобы решить эту проблему, нужно поднять эту проблему в списке рассылки идей Python, но будьте готовы много спорить, чтобы набрать обороты.

На самом деле, просто чтобы не сказать "нет выхода", третий способ может заключаться в реализации еще одного уровня лямбда-вызова, просто чтобы развернуть параметры - но это будет одновременно более неэффективно и сложнее для чтения, чем ваши два предложения:

min(points, key=lambda p: (lambda x,y: (x*x + y*y))(*p))

обновить Python 3.8

На данный момент доступен Python 3.8 alpha1 и реализованы выражения присваивания PEP 572-.

Итак, если кто-то использует трюк для выполнения нескольких выражений внутри лямбды - я обычно делаю это, создавая кортеж и просто возвращая последний его компонент, это можно сделать:

>>> a = lambda p:(x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
>>> a((3,4))
25

Следует помнить, что такой код редко бывает более читабельным или практичным, чем полноценная функция. Тем не менее, возможны варианты использования - если существуют различные однострочные символы, которые будут работать с этим point, возможно, стоит иметь именованный кортеж и использовать выражение присваивания для эффективного "приведения" входящей последовательности к именованному кортежу:

>>> from collections import namedtuple
>>> point = namedtuple("point", "x y")
>>> b = lambda s: (p:=point(*s), p.x ** 2 + p.y ** 2)[-1]

Ответ 2

В соответствии с http://www.python.org/dev/peps/pep-3113/ распаковка распаковки исчезает, а 2to3 будет переводить их так:

Поскольку параметры кортежа используются lambdas из-за единственного ограничение выражения, они также должны поддерживаться. Это делается путем имеющий ожидаемый аргумент последовательности, связанный с одним параметром и затем индексирование по этому параметру:

lambda (x, y): x + y

будет переведено на:

lambda x_y: x_y[0] + x_y[1]

Это очень похоже на вашу реализацию.

Ответ 3

Я не знаю никаких хороших общих альтернатив распаковке поведения аргументов Python 2. Вот пара предложений, которые могут быть полезны в некоторых случаях:

  • если вы не можете вспомнить имя; используйте имя параметра ключевого слова:

    def key(p): # more specific name would be better
        x, y = p
        return x**2 + y**3
    
    result = min(points, key=key)
    
  • вы могли бы видеть, если namedtuple делает ваш код более читаемым, если список используется в нескольких местах:

    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)
    

Ответ 4

Хотя аргументы деструктуризации были удалены в Python3, он не был удален из понятий. Можно злоупотреблять им, чтобы получить аналогичное поведение на Python 3. По сути, мы используем тот факт, что совлокальные подпрограммы позволяют нам вывернуть функции наизнанку, а доходность не является утверждением и, следовательно, разрешена внутри lambdas.

Например:

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))))

По сравнению с принятым ответом на использование обертки, это решение может полностью разрушить аргументы, в то время как оболочка разрушает только первый уровень. То есть,

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))))

По сравнению с

values = [(('A',1),'a'), (('B',0),'b')]
print(min(points, key=lambda p: (lambda a,b: (lambda x,y: (y))(*a))(*p)))

В качестве альтернативы можно также

values = [(('A',1),'a'), (('B',0),'b')]
print(min(points, key=lambda y: next(b for (a,b),c in [y])))

Или немного лучше

print(min(values, key=lambda y: next(b for ((a,b),c) in (y,))))

Это просто означает, что это можно сделать и не следует воспринимать как рекомендацию.

Ответ 5

Основываясь на предложениях Cuadue и ваших комментариях о распаковке, которые все еще присутствуют в понимании, вы можете использовать, используя numpy.argmin:

result = points[numpy.argmin(x*x + y*y for x, y in points)]

Ответ 6

Другой вариант - записать его в генератор, производящий кортеж, где ключ является первым элементом. Сравнение кортежей начинается от начала до конца, поэтому возвращается кортеж с наименьшим первым элементом. Затем вы можете проиндексировать результат, чтобы получить значение.

min((x * x + y * y, (x, y)) for x, y in points)[1]

Ответ 7

Подумайте, нужно ли вам сначала распаковать кортеж:

min(points, key=lambda p: sum(x**2 for x in p))

или нужно ли указывать явные имена при распаковке:

min(points, key=lambda p: abs(complex(*p))

Ответ 8

Я думаю, что лучший синтаксис - это x * x + y * y let x, y = point, ключевое слово let следует выбирать более тщательно.

Двойная лямбда - самая близкая версия. lambda point: (lambda x, y: x * x + y * y)(*point)

Вспомогательная функция высокого порядка будет полезна, если мы дадим ей правильное имя.

def destruct_tuple(f):
  return lambda args: f(*args)

destruct_tuple(lambda x, y: x * x + y * y)