람다에서 자동 튜플 풀기에 해당하는 좋은 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)
'Program Tip' 카테고리의 다른 글
Eclipse 콘솔에서 백 스페이스 \ b를 작동시키는 방법은 무엇입니까? (0) | 2020.10.17 |
---|---|
POSIX 스레드 및 신호 (0) | 2020.10.17 |
HttpServlet이 Serializable을 구현하는 이유는 무엇입니까? (0) | 2020.10.17 |
이미 레이블이 지정된 입력 요소에 "aria-labelledby"를 사용하는 목적은 무엇입니까? (0) | 2020.10.17 |
AngularJS에서 바인딩 및 다이제스트는 어떻게 작동합니까? (0) | 2020.10.17 |