在lambda中自动元组解包的等效python3有什么用?


92

考虑以下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中删除此功能会强制代码执行丑陋的方式(使用魔术索引)或创建不必要的功能(最麻烦的部分是为这些不必要的功能想到好名字)

我认为该功能至少应单独添加回lambda。有没有好的选择?


更新:我正在使用以下帮助程序扩展答案中的想法

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完全有可能将其从语言中删除,然后撤消使它更难使用的更改,但是如果您想表达一下希望看到添加的功能,可以尝试在python-ideas上发布。
Wooble 2014年

3
我也不明白,但BDFL似乎与lambda他反对的精神相同mapreduce并且filter
Cuadue 2014年

3
lambda计划在py3k中删除,因为它基本上是该语言的主要缺陷。但是没有人能就定义匿名功能的合适选择达成共识,因此,Guido最终屈服,就是这样。
roippi 2014年

5
匿名函数是任何适当语言中必不可少的,我非常喜欢lambda。我将不得不阅读这场辩论的原因。(此外,即使map并且filter最好用理解力代替,我也喜欢reduce
njzk2 2014年

13
我不喜欢Python 3的一件事
timgeb

Answers:


32

不,没有其他办法。您覆盖了所有内容。可行的方法是在Python Ideas邮件列表中提出此问题,但准备在那儿进行很多辩论以获得一定的吸引力。

实际上,只是不说“没有出路”,第三种方法可能是实现一个更高级别的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]

3
当然,我忘了提到这样做的“一个明显”方法实际上是使用defnme问题中提到的那三行函数some_name_to_think-of
jsbueno '16

2
这个答案仍然看到一些访客-使用PEP 572实施,应该有办法在lambda表达式内创建变量,例如:key = lambda p: (x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
jsbueno

1
作业表达式!!我(很高兴)惊讶他们取得了成功
javadba

23

根据http://www.python.org/dev/peps/pep-3113/的说明,元组解包已不复存在,2to3并将像这样翻译它们:

由于单表达式限制,lambda使用元组参数,因此也必须支持它们。这是通过将期望的序列参数绑定到单个参数,然后对该参数建立索引来完成的:

lambda (x, y): x + y

将被翻译成:

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

这与您的实现非常相似。


3
好的是不要将其删除以进行循环或理解
balki 2014年

11

我不知道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在多个位置使用该列表,则可以看到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是此处的最佳做法。您不仅可以保持你的拉姆达而且我发现namedtuple版本是身份的区别更具可读性lambda (x, y):lambda x, y:不明显一见钟情。
Burak Arslan '18年

5

虽然在Python3中删除了解构参数,但并未将其从理解中删除。可能会滥用它以在Python 3中获得类似的行为。从本质上讲,我们利用了以下事实:协例程使我们能够将函数内翻,而yield不是语句,因此可以在lambda中使用。

例如:

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 = pointlet应该更谨慎地选择关键字。

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)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.