在lambda中自动元组解包的好等价python3是什么?

时间:2014-02-19 21:36:42

标签: python python-3.x

考虑以下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

8 个答案:

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