Program Tip

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

programtip 2020. 10. 17. 12:02
반응형

람다에서 자동 튜플 풀기에 해당하는 좋은 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

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

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

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

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

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

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]

구현과 매우 유사합니다.


I don't know any good general alternatives to the Python 2 arguments unpacking behaviour. Here's a couple of suggestion that might be useful in some cases:

  • if you can't think of a name; use the name of the keyword parameter:

    def key(p): # more specific name would be better
        x, y = p
        return x**2 + y**3
    
    result = min(points, key=key)
    
  • you could see if a namedtuple makes your code more readable if the list is used in multiple places:

    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)
    

While the destructuring arguments was removed in Python3, it was not removed from comprehensions. It is possible to abuse it to obtain similar behavior in Python 3. In essence, we take advantage of the fact that co-routines allow us to turn functions inside out, and yield is not a statement, and hence is allowed within lambdas.

For example:

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

In comparison with the accepted answer of using a wrapper, this solution is able to completely destructure the arguments while the wrapper only destructures the first level. That is,

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

In comparison to

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

Alternatively one can also do

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

Or slightly better

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

This is just to suggest that it can be done, and should not be taken as a recommendation.


Based on Cuadue suggestion and your comment on unpacking still being present in comprehensions, you can use, using numpy.argmin :

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

Another option is to write it into a generator producing a tuple where the key is the first element. Tuples are compared starting from beginning to end so the tuple with the smallest first element is returned. You can then index into the result to get the value.

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

Consider whether you need to unpack the tuple in the first place:

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

or whether you need to supply explicit names when unpacking:

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

I think the better syntax is x * x + y * y let x, y = point, let keyword should be more carefully chosen.

The double lambda is the closest version. lambda point: (lambda x, y: x * x + y * y)(*point)

High order function helper would be useful in case we give it a proper name.

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

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

참고URL : https://stackoverflow.com/questions/21892989/what-is-the-good-python3-equivalent-for-auto-tuple-unpacking-in-lambda

반응형