如何使用 3.x 模拟 2.x 对 lambda 参数的元组解包?

2025-02-27 09:06:00
admin
原创
56
摘要:问题描述:在 Python 2 中,我可以写:In [5]: points = [ (1,2), (2,3)] In [6]: min(points, key=lambda (x, y): (x*x + y*y)) Out[6]: (1, 2) 但是 3.x 版本不支持此功能: File "&l...

问题描述:

在 Python 2 中,我可以写:

In [5]: points = [ (1,2), (2,3)]

In [6]: min(points, key=lambda (x, y): (x*x + y*y))
Out[6]: (1, 2)

但是 3.x 版本不支持此功能:

  File "<stdin>", line 1
    min(points, key=lambda (x, y): (x*x + y*y))
                           ^
SyntaxError: invalid syntax

直接的解决方法是对传递的元组进行明确索引:

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

但是由于lambda仅支持单个表达式,因此无法将x, y = p部分放入其中。

我还能怎样解决这个限制?


解决方案 1:

不,没有其他办法。您已经全部解决了。解决方法是在Python ideas 邮件列表上提出这个问题,但要做好在那里争论不休的准备,以获得一些支持。

实际上,不仅仅是说“没有出路”,第三种方法可能是实现另一个级别的 lambda 调用以展开参数 - 但这比你的两个建议更有效率且更难阅读:

min(points, key=lambda p: (lambda x,y: (x*x + y*y))(*p))

Python 3.8 更新

自 Python 3.8 发布以来,PEP 572(赋值表达式)已作为一种工具使用。

因此,如果使用技巧在 lambda 中执行多个表达式 - 我通常通过创建一个元组并仅返回它的最后一个组件来实现,则可以执行以下操作:

>>> a = lambda p:(x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
>>> a((3,4))
25

应该记住,这种代码很少会比具有完整函数的代码更易读或更实用。不过,还是有用途的 - 如果有各种可以对此进行操作的单行代码point,则值得拥有一个namedtuple,并使用赋值表达式有效地将传入序列“投射”到命名元组:

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

解决方案 2:

根据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:

我不知道有什么好的通用方法可以替代 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)

解决方案 4:

虽然解构参数在 Python3 中被移除了,但它并没有从推导式中移除。在 Python 3 中,可以滥用它来获得类似的行为。

例如:

points = [(1,2), (2,3)]
print(min(points, key=lambda y: next(x*x + y*y for (x,y) in [y])))

与使用包装器的可接受答案相比,此解决方案能够完全解构参数,而包装器仅解构第一级。也就是说,你可以这样做

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

与使用解包器 lambda 的公认答案相比:

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

这只是建议这样做,不应视为建议。但是,在我看来,这比在元组中使用多个表达式并返回最后一个表达式要好。

解决方案 5:

我认为语法越好x * x + y * y let x, y = pointlet关键字的选择就越谨慎。

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

解决方案 6:

由于 Stack Overflow 上的问题不应该包含答案,也不应该有明确的“更新”部分,因此我将 OP 的原始“更新”转换为正确的答案并将其作为社区 wiki。

OP 最初声称这个解决方案是“扩展答案中的想法”。我无法辨别这意味着哪个答案或哪个想法。这个想法在功能上与anthony.hl 的答案相同,但那是几年后的事情了。考虑到当时答案的状态,我认为这符合 OP 的原创作品。)


创建一个包装函数来概括解包参数的过程,如下所示:

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

现在我们可以使用它来将我们想要编写的 lambda 转换为可以正确接收参数的 lambda:

min(points, key=star(lambda x,y: (x*x + y*y))

我们可以使用functools.wraps进一步清理它:

import functools

def star(f):
    @functools.wraps(f)
    def f_inner(args):
        return f(*args)
    return f_inner

解决方案 7:

首先考虑一下是否需要解包元组:

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

或者在解包时是否需要提供明确的名称:

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

解决方案 8:

根据 Cuadue 的建议和您对解包仍然存在于理解中的评论,您可以使用numpy.argmin

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

解决方案 9:

另一种选择是将其写入生成器,生成一个元组,其中键是第一个元素。从头到尾比较元组,因此返回具有最小第一个元素的元组。然后您可以索引结果以获取值。

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

解决方案 10:

使用PyFunctional可能确实可以解决这个问题!

虽然目前不支持,但我已经提交了元组参数解包功能请求以支持:

(
    seq((1, 2), (3, 4))
    .map(unpack=lambda a, b: a + b)
)  # => [3, 7]
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3878  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2714  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   49  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   49  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   49  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用