람다에서 자동 튜플 풀기에 해당하는 좋은 python3은 무엇입니까?


94

다음 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

4
lambda언어에서 완전히 제거 된 다음 사용하기 더 어렵게 만든 변경 사항을 되돌릴 가능성이 더 높지만 기능이 다시 추가되는 것을보고 싶은 욕구를 표현하고 싶다면 파이썬 아이디어에 게시 해 볼 수 있습니다.
Wooble

3
나도 그것을 얻을하지 않습니다,하지만 BDFL이 반대하는 것처럼 보인다 lambda그가 반대와 같은 정신 map, reduce그리고 filter.
Cuadue 2014

3
lambda기본적으로 언어에 대한 역병이기 때문에 py3k에서 제거 될 예정입니다. 그러나 아무도 익명 기능을 정의하는 적절한 대안에 동의 할 수 없었기 때문에 결국 귀도 는 패배로 팔을 내 밀었습니다 .
roippi

5
익명 함수는 적절한 언어에 필수이며 람다를 아주 좋아합니다. 나는 그러한 논쟁의 이유를 읽어야 할 것입니다. (또한, map그리고 filter이해력으로 가장 잘 대체되지만, 나는 좋아합니다 reduce)
njzk2

13
내가 파이썬 3에 대해 싫어하는 한 가지는 ...
timgeb

답변:


33

아니, 다른 방법은 없습니다. 당신은 모든 것을 다뤘습니다. 갈 길은이 문제를 파이썬 아이디어 메일 링리스트 에 올리는 것입니다 .하지만 거기에서 관심을 끌기 위해 많은 논쟁을 할 준비를하세요.

사실, "출구가 없다"라고 말하지 않고, 세 번째 방법은 매개 변수를 펼치기 위해 하나 이상의 람다 호출 수준을 구현하는 것입니다.하지만 이는 두 가지 제안보다 비효율적이고 읽기 어렵습니다.

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

이런 종류의 코드는 완전한 기능을 갖는 것보다 더 읽기 쉽고 실용적이지 않다는 것을 명심해야합니다. 여전히 가능한 사용이 있습니다. 만약 이것에 대해 작동 할 다양한 one- pointliners가 있다면 namedtuple을 가지고 할당 표현식을 사용하여 들어오는 시퀀스를 namedtuple로 효과적으로 "캐스트"하는 것이 좋습니다.

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

3
그리고 물론, 나는 이것을하는 "하나의 명백한"방법은 실제로 defnme 아래의 질문에 언급 된 것을 사용하여 3 줄 함수를 사용 하는 것임을 언급 하는 것을 잊었다 some_name_to_think-of.
jsbueno

2
이 답변은 여전히 ​​일부 방문자를 볼 수 있습니다. PEP 572 구현을 사용하면 다음과 같이 람다 식 내부에 변수를 만드는 방법이 있어야합니다.key = lambda p: (x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
jsbueno

1
대입 식 !! 나는 (즐겁게) 그들이 그것을 만들었다 놀랐습니다
javadba

24

http://www.python.org/dev/peps/pep-3113/ 에 따르면 tuple unpacking이 사라지고 2to3다음과 같이 번역됩니다.

튜플 매개 변수는 단일 식 제한으로 인해 람다에서 사용되므로 지원해야합니다. 이는 예상되는 시퀀스 인수를 단일 매개 변수에 바인드 한 다음 해당 매개 변수를 인덱싱하여 수행됩니다.

lambda (x, y): x + y

다음으로 번역됩니다.

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

귀하의 구현과 매우 유사합니다.


3
for 루프 또는 comprehensions에서 제거되지 않는 것이 좋습니다
balki

12

파이썬 2 인수 압축 해제 동작에 대한 좋은 일반적인 대안을 모릅니다. 다음은 경우에 따라 유용 할 수있는 몇 가지 제안입니다.

  • 이름이 생각 나지 않는다면 키워드 매개 변수의 이름을 사용하십시오.

    def key(p): # more specific name would be better
        x, y = p
        return x**2 + y**3
    
    result = min(points, key=key)
  • namedtuple목록이 여러 위치에서 사용되는 경우 a 가 코드를 더 읽기 쉽게 만드는지 확인할 수 있습니다.

    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)

지금까지이 질문에 대한 모든 답변 중에서 명명 된 튜플을 사용하는 것이 가장 좋은 방법입니다. 뿐만 아니라 당신은 당신의 람다를 유지할 수 있습니다뿐만 아니라 나는 namedtuple 버전의 차이로 더 읽을 것을 발견 lambda (x, y):하고 lambda x, y:첫눈에 명확하지 않다.
Burak Arslan

5

비 구조화 인수는 Python3에서 제거되었지만 이해에서 제거되지 않았습니다. 파이썬 3에서 유사한 동작을 얻기 위해 그것을 남용 할 수 있습니다. 본질적으로, 우리는 코 루틴이 우리가 함수를 뒤집을 수 있도록 허용하고 yield는 진술이 아니므로 람다 내에서 허용된다는 사실을 이용합니다.

예를 들면 :

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

이것은 단지 할 수 있다는 것을 암시하기위한 것이며 권장 사항으로 받아 들여서는 안됩니다.


0

Cuadue 제안과 압축 풀기에 대한 귀하의 의견을 바탕으로 여전히 이해하기 위해 다음을 사용할 수 있습니다 numpy.argmin.

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

0

또 다른 옵션은 키가 첫 번째 요소 인 튜플을 생성하는 생성기에 작성하는 것입니다. 튜플은 처음부터 끝까지 비교되므로 첫 번째 요소가 가장 작은 튜플이 반환됩니다. 그런 다음 결과를 색인화하여 값을 얻을 수 있습니다.

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

0

처음에 튜플을 풀어야하는지 고려하십시오.

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

또는 압축을 풀 때 명시적인 이름을 제공해야하는지 여부 :

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

0

더 나은 구문은 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)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.