ラムダで自動タプルをアンパックするのに適したpython3の同等物は何ですか?


94

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

4
おそらくlambda、言語から完全に削除されてから、使用が困難になった変更を元に戻す可能性が高くなりますが、機能が再び追加されることを確認したい場合は、python-ideasに投稿してみてください。
Wooble

3
私はどちらかそれを得ることはありませんが、BDFLは反対のように思えるlambda彼が反対するのと同じ精神でmapreducefilter
Cuadue 2014

3
lambdapy3kでの削除が予定されていました。これは基本的に言語の問題であるためです。しかし、匿名関数を定義するための適切な代替案については誰も合意できなかったため、最終的にグイドは敗北に腕を上げ、それがそれでした。
roippi 2014

5
匿名関数は適切な言語では必須であり、ラムダがとても好きです。私はそのような議論の理由を読まなければならないでしょう。(また、にもかかわらずmapとはfilter最高の内包表記に置き換えられ、私は好きですreduce
njzk2

13
Python 3で私が嫌いなことの1つは...
timgeb

回答:


33

いいえ、他に方法はありません。あなたはそれをすべてカバーしました。行く方法は、Pythonアイデアメーリングリストでこの問題を提起することですが、いくつかの牽引力を得るために、あちらこちらで多くのことを議論する準備をしてください。

実際、「出口はありません」とは言わないだけでなく、3つ目の方法は、パラメーターを展開するためにラムダコールのレベルをもう1つ実装することです。

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

この種のコードは、完全な機能を持つよりも読みやすく、実用的ではない場合があることに注意してください。それでも、可能な使用point法があります-thisで動作するさまざまなワンライナーがある場合は、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
そしてもちろん、これを行うための「明白な」方法はdef、nmeの下の質問で言及されているそれを使用して3行の関数を実際に費やすことであることを言及するのを忘れていましたsome_name_to_think-of
jsbueno 2016年

2
この答えは、まだいくつかの訪問者を参照してください- PEP 572実装と、ラムダ式の内部変数を作成する方法があるはず、のように:key = lambda p: (x:=p[0], y:=p[1], x ** 2 + y ** 2)[-1]
jsbueno

1
代入式!! 私は(喜んで)彼らが
成功し

24

http://www.python.org/dev/peps/pep-3113/によると、タプルのアンパックはなくなり、次の2to3ように変換されます:

タプルパラメーターは単一の式の制限のためにラムダによって使用されるため、それらもサポートする必要があります。これは、予想されるシーケンス引数を単一のパラメーターにバインドし、そのパラメーターにインデックスを付けることによって行われます。

lambda (x, y): x + y

に翻訳されます:

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

これは、実装に非常に似ています。


3
forループまたは内包表記で削除されないことは良いことです
バルキ

12

私は、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)
    

これまでのところ、この質問に対するすべての回答の中で、namedtupleを使用することがここでの最善の行動方針です。だけでなく、あなたのラムダを保つことができるだけでなく、私はnamedtupleバージョンは違い、より読み取り可能であることを発見lambda (x, y):し、lambda x, y:一目で明らかにされていません。
Burak Arslan、

5

破壊的な引数はPython3で削除されましたが、内包表記からは削除されませんでした。これを悪用してPython 3で同様の動作を取得することは可能です。本質的に、コルーチンによって関数を裏返しにすることができ、yieldはステートメントではないため、ラムダ内で許可されます。

例えば:

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キーワードはより慎重に選択する必要があります。

ダブルラムダは最も近いバージョンです。 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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.