考虑以下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)
这非常难看。如果lambda是一个函数,我可以做
def some_name_to_think_of(p):
x, y = p
return x*x + y*y
在python3中删除此功能会强制代码执行丑陋的方式(使用魔术索引)或创建不必要的函数(最令人烦恼的部分是为这些不必要的函数考虑好名称)
我认为该功能至少应该添加到lambdas中。有没有好的选择?
更新:我正在使用以下帮助器来扩展答案中的想法
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
答案 0 :(得分:26)
不,没有别的办法。你完全覆盖了它。要走的路是在Python ideas mailing list上提出这个问题,但要准备在那里争论很多以获得一些牵引力。
实际上,只是不要说“没有出路”,第三种方式可能是实现一个更多级别的lambda调用只是为了展开参数 - 但这会比你的更低效,更难阅读两个建议:
min(points, key=lambda p: (lambda x,y: (x*x + y*y))(*p))
更新Python 3.8
到目前为止,Python 3.8 alpha1已经可用,并且实现了PEP 572-赋值表达式。
所以,如果一个人使用技巧在lambda中执行多个表达式 - 我通常通过创建一个元组并返回它的最后一个组件来做到这一点,可以这样做:
>>> a = lambda p:(x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
>>> a((3,4))
25
应该记住,这种代码很少比具有完整功能更具可读性或实用性。仍然有一些可能的用途 - 如果有各种单行可以在这个point
上运行,那么有一个namedtuple可能是值得的,并使用赋值表达式有效地“将”传入序列“强制转换”到namedtuple :
>>> from collections import namedtuple
>>> point = namedtuple("point", "x y")
>>> b = lambda s: (p:=point(*s), p.x ** 2 + p.y ** 2)[-1]
答案 1 :(得分:21)
根据http://www.python.org/dev/peps/pep-3113/元组解包已经消失,而2to3
会将其翻译成:
因为lambdas使用了元组参数,因为单个参数 表达式限制,它们也必须得到支持。这是通过 将预期的序列参数绑定到单个参数和 然后索引该参数:
lambda (x, y): x + y
将被翻译成:
lambda x_y: x_y[0] + x_y[1]
这与您的实施非常相似。
答案 2 :(得分:8)
我不知道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)
答案 3 :(得分:4)
虽然在Python3中删除了解构参数,但它并没有从理解中删除。有可能滥用它来获得Python 3中的类似行为。本质上,我们利用了协同例程允许我们将函数内部转换出来的事实,而yield不是一个语句,因此在lambdas中是允许的。 / p>
例如:
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,))))
这只是建议可以做到,而不应被视为推荐。
答案 4 :(得分:0)
根据Cuadue的建议和您对解包的解释仍然存在于理解中,您可以使用numpy.argmin
:
result = points[numpy.argmin(x*x + y*y for x, y in points)]
答案 5 :(得分:0)
另一个选择是将其写入生成元组的生成器,其中键是第一个元素。从头到尾比较元组,以便返回第一个元素最小的元组。然后,您可以索引结果以获取值。
min((x * x + y * y, (x, y)) for x, y in points)[1]
答案 6 :(得分:0)
首先考虑是否需要打开元组的包装:
min(points, key=lambda p: sum(x**2 for x in p))
或在打开包装时是否需要提供显式名称:
min(points, key=lambda p: abs(complex(*p))
答案 7 :(得分:0)
我认为更好的语法是x * x + y * y let x, y = point
,应该更谨慎地选择let
关键字。
double lambda是最接近的版本。
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)