Pythonに複数行のLambdaがない:なぜそうしないのですか?


334

複数行のラムダは、Pythonの他の構文構造と構文的に衝突するため、Pythonでは追加できないと言われています。今日バスでこれについて考えていたとき、複数行のラムダが衝突する単一のPython構成を考えることができないことに気づきました。私は言語をかなりよく知っているので、これは私を驚かせました。

さて、Guidoが言語に複数行のラムダを含めない理由があるのは確かですが、好奇心からです。複数行のラムダを含めることが曖昧になる状況は何ですか?私が聞いたことは本当ですか、それともPythonが複数行のラムダを許可しない他の理由がありますか?


12
tl; drバージョン: Pythonは{}ブロックのない遅延言語なので、一貫した構文設計を維持するためにこれは許可されていませんでした。
アンドリュー

11
また、私は答えで誰もこれに言及しなかったことに完全に驚いています... Pythonで\文字で行を終了し、次の行に続けることができます...この情報は、この質問全体を少し優先するので...
Andrew


「統語論的デザイン」
ニコラス

これには、式内でステートメントを許可する必要があります。それを行う場合lambda、そもそも式は必要ありません。def式でステートメントを使用するだけです。
chepner

回答:


153

以下を見てください。

map(multilambda x:
      y=x+1
      return y
   , [1,2,3])

これはラムダが戻りますか(y, [1,2,3])(したがって、マップは1つのパラメータしか取得しないため、エラーが発生します)?それとも戻りyますか?または、新しい行のカンマが誤って配置されているため、構文エラーですか?Pythonはあなたが望むものをどのようにして知るのでしょうか?

括弧内では、インデントはpythonにとって重要ではないため、マルチラインを明確に操作することはできません。

これは単純なもので、おそらくもっと多くの例があります。


106
ラムダからタプルを返したい場合は、括弧の使用を強制できます。IMO、これはそのようなあいまいさを防ぐために常に実施されるべきでしたが、まあ。
mpen 2012

26
これは単純な曖昧さであり、括弧の追加セットを追加することで解決する必要があります。たとえば、他の引数で囲まれたジェネレータ式、整数リテラルでメソッドを呼び出すなど、これは必ずしも必要ではありません。関数名は数字で始めることはできません)、もちろん単一行のラムダ(複数行に記述された長い式である場合もあります)も同様です。複数行のラムダは、これらのケースに基づいて除外することが保証されている場合と特に異なりません。これが本当の答えです。
nmclean 2014年

3
私はそれをまったく心配せずに処理する無数の言語があるのが好きですが、どういうわけかそれが不可能ではないにしても非常に難しいと思われるいくつかの深い理由があります
ニコラ

1
一言で言えばpythonである
@nicolas

ラムダを使用しない理由は、Pythonであまり開発されていないためです。
NoName

634

Guido van Rossum(Pythonの発明者)はこの正確な質問に自分で古いブログの投稿で答えています。
基本的に、彼はそれが理論的には可能であると認めますが、提案された解決策はPythonではありません:

「しかし、このパズルのために提案されたソリューションの複雑さは計り知れません。それは、パーサー(より正確にはレクサー)がインデント依存モードとインデント非依存モードを切り替えて、スタックを維持できるようにする必要があります以前のモードとインデントレベルの問題です。技術的にはすべて解決できます(一般化できるインデントレベルのスタックは既に存在します)。しかし、どれもそれがすべて精巧なRube Goldbergの仕掛けであるという私の直感を取り除きません。」


108
なぜこれが一番の答えではないのですか?発明者が明確に述べたように、それは技術的な理由ではなく、設計上の選択です。
Dan Abramov、

13
OPがおそらく何年もログインしなかったため、@ DanAbramov。
ファルケン教授の契約が2013年

7
Rube Goldbergのリファレンスを理解していなかった方は、en.wikipedia.org
wiki / Rube_Goldberg_Machine

56
グイドの答えは、Pythonがブロックを定義するためにインデントに依存しないことを望むもう1つの理由です。
LS

25
「ガット感」をデザインの選択と呼ぶかどうかはわかりません。;)
Elliot Cameron

54

これは一般に非常に醜いです(ただし、代替案はさらに醜い場合もあります)。

lambda: (
    doFoo('abc'),
    doBar(123),
    doBaz())

ただし、割り当てを受け付けないため、事前にデータを準備する必要があります。私がこれが便利だと思ったのは、PySideラッパーです。このラッパーでは、短いコールバックがある場合があります。追加のメンバー関数を作成すると、さらに醜くなります。通常、これは必要ありません。

例:

pushButtonShowDialog.clicked.connect(
    lambda: (
    field1.clear(),
    spinBox1.setValue(0),
    diag.show())

2
私の上司は、PyQtアプリケーションでこのようなものを求めていました。驚くばかり!
TheGerm、2015年

1
このおかげで、私はPySide UIのコールバックとして短い(ただし複数行の)ラムダを使用する良い方法も探していました。
Michael Leonard

そして今、これを見てlambda argsetattr(arg, 'attr','value')「割り当てなし...」を使用して転覆させることを即座に提案しました。そして、それの短絡評価がandありorます...それを行うのはJavascriptです。壁にツタのように根をあなたに沈めます。私は私がこれをクリスマス以上に忘れることをほぼ望んでいます。
nigel222 2018

かなり賢い-そしてかなり読みやすい。今-それらの(欠けている..)割り当てについて ..
javadba

@ nigel222なぜ恥ずかしいと思う?Python言語は根本的に不自由ですが、とにかく多くのデータサイエンスで使用されています。したがって、調整を行います。副作用(多くの場合、単に/ loggingを出力するだけで十分です)と割り当て(多くの場合、中間変数だけで十分です)を行う方法を見つけることは、言語によって適切に処理されます。しかし、それらはサポートされていません(少なくともPEPガイドラインに従っている場合)
javadba

17

いくつかの関連リンク:

しばらくの間、私はReiaの開発を追跡していました。Reiaは最初、Pythonのインデントベースの構文とRubyブロックもすべてErlangの上に置く予定でした。しかし、デザイナーはインデントの敏感さをあきらめました。この決定について彼が書いたこの投稿には、インデント+複数行のブロックで遭遇した問題についての議論と、Guidoのデザインの問題/決定について得られた評価の増加が含まれています。

http://www.unlimitednovelty.com/2009/03/indentation-sensitiveivity-post-mortem.html

また、ここで私が遭遇したPythonのRubyスタイルブロックの興味深い提案があります。Guidoは実際にそれを撃墜せずに応答を投稿します(ただし、その後の撃墜があったかどうかはわかりません)。

http://tav.espians.com/ruby-style-blocks-in-python.html


12

[編集] この回答を読んでくださいそれはなぜマルチラインラムダが事ではないのかを説明しています。

簡単に言えば、それは非Pythonicです。グイドファンロッサムのブログ投稿から:

式の途中にインデントベースのブロックを埋め込むという解決策は受け入れられません。ステートメントのグループ化のための代替構文(中かっこやbegin / endキーワードなど)も同様に受け入れられないので、これにより、複数行のラムダが解決できないパズルになります。


10

素晴らしいけれど恐ろしいハックをお見せしましょう。

import types

def _obj():
  return lambda: None

def LET(bindings, body, env=None):
  '''Introduce local bindings.
  ex: LET(('a', 1,
           'b', 2),
          lambda o: [o.a, o.b])
  gives: [1, 2]

  Bindings down the chain can depend on
  the ones above them through a lambda.
  ex: LET(('a', 1,
           'b', lambda o: o.a + 1),
          lambda o: o.b)
  gives: 2
  '''
  if len(bindings) == 0:
    return body(env)

  env = env or _obj()
  k, v = bindings[:2]
  if isinstance(v, types.FunctionType):
    v = v(env)

  setattr(env, k, v)
  return LET(bindings[2:], body, env)

これで、このLETフォームをそのまま使用できます。

map(lambda x: LET(('y', x + 1,
                   'z', x - 1),
                  lambda o: o.y * o.z),
    [1, 2, 3])

それは与える: [0, 3, 8]



これはすごい!私は次回Pythonを書くときにこれを使うと思います。私は主にLispとJSのプログラマーであり、複数行のラムバダの欠如が痛いです。これはそれを取得する方法です。
クリストファーデュマ2017

7

私は私のプロジェクトのいくつかでこの汚いハックを実践することで罪があります。

    lambda args...:( expr1, expr2, expr3, ...,
            exprN, returnExpr)[-1]

私はあなたがpythonicを維持する方法を見つけることができることを願っていますが、そうする必要がある場合は、execを使用してグローバルを操作するよりもこれほど簡単です。


6

@balphaの解析問題に取り組みましょう。マルチラインラムダを括弧で囲みます。括弧がない場合、ラムダの定義は貪欲です。だからラムダ

map(lambda x:
      y = x+1
      z = x-1
      y*z,
    [1,2,3]))

を返す関数を返します (y*z, [1,2,3])

だが

map((lambda x:
      y = x+1
      z = x-1
      y*z)
    ,[1,2,3]))

手段

map(func, [1,2,3])

funcはy * zを返す複数行のラムダです。それは動作しますか?


1
map(func, [1,2,3])map関数に十分な引数がないため、上の方が返り、下の方がエラーになるはずだと思います。また、コード内にいくつかの余分な括弧があります。
Samy Bencherif 2016

python2.7.13を実行しているpycharmにドロップすると、構文エラーが発生します。
simbo1905 2018

余分な括弧
Samy Bencherif

4

(このトピックにまだ興味がある人のために。)

これを検討してください(「マルチライン」ラムダ内の他のステートメントでのステートメントの戻り値の使用法も含まれますが、嘔吐するほど醜いです;-)

>>> def foo(arg):
...     result = arg * 2;
...     print "foo(" + str(arg) + ") called: " + str(result);
...     return result;
...
>>> f = lambda a, b, state=[]: [
...     state.append(foo(a)),
...     state.append(foo(b)),
...     state.append(foo(state[0] + state[1])),
...     state[-1]
... ][-1];
>>> f(1, 2);
foo(1) called: 2
foo(2) called: 4
foo(6) called: 12
12

これは、異なるパラメーターを使用して2回目に呼び出さstate.clear()れた場合は機能せず、デフォルトの引数は関数の作成時に1回しか作成されないため、最初の行でない限り、メモリリークが発生します。
Matthew D. Scholefield、2018

1

\ラムダ関数に複数の行がある場合は、単にスラッシュ()を使用できます

例:

mx = lambda x, y: x if x > y \
     else y
print(mx(30, 20))

Output: 30

問題は、複数のリテラル行ではなく複数の式を使用することに関係しています。
Tomas Zubiri

1

私はpythonから始めていますが、Javascriptからの最も明白な方法は、式を関数として抽出することです...

考案された例では、乗算式(x*2)が関数として抽出されるため、複数行を使用できます。

def multiply(x):
  print('I am other line')
  return x*2

r = map(lambda x : multiply(x), [1, 2, 3, 4])
print(list(r))

https://repl.it/@datracka/python-lambda-function

多分それがラムダ式自体複数行を行う方法であったかどうかの質問に正確に答えないかもしれませんが、誰かがこのスレッドを取得して式をデバッグする方法を探している場合(私のように)、それが役立つと思います


2
なぜそれを書いて、単に書くのではないのmap(multiply, [1, 2, 3])ですか?
トタール

0

醜いハックについては、execと通常の関数の組み合わせを使用して、次のような複数行の関数を定義できます。

f = exec('''
def mlambda(x, y):
    d = y - x
    return d * d
''', globals()) or mlambda

これを次のような関数にラップできます。

def mlambda(signature, *lines):
    exec_vars = {}
    exec('def mlambda' + signature + ':\n' + '\n'.join('\t' + line for line in lines), exec_vars)
    return exec_vars['mlambda']

f = mlambda('(x, y)',
            'd = y - x',
            'return d * d')

0

私は少し減らして、dictでreduceを理解しようとして、次の1つのライナーハックを思いついただけです。

In [1]: from functools import reduce
In [2]: reduce(lambda d, i: (i[0] < 7 and d.__setitem__(*i[::-1]), d)[-1], [{}, *{1:2, 3:4, 5:6, 7:8}.items()])                                                                                                                                                                 
Out[3]: {2: 1, 4: 3, 6: 5}

私はこのJavascript dict内包で行われたのと同じことをやろうとしていました:https : //stackoverflow.com/a/11068265


0

マルチラインラムダのより興味深い実装を次に示します。Pythonがコードを構造化する方法としてインデントをどのように使用するかにより、達成することは不可能です。

しかし、幸運なことに、インデントの書式設定は配列と括弧を使用して無効にできます。

すでに指摘したように、コードは次のように書くことができます。

lambda args: (expr1, expr2,... exprN)

理論的には、左から右への評価が保証されている場合は機能しますが、1つの式から別の式に渡される値は失われます。

もう少し冗長なものを実現する1つの方法は、

lambda args: [lambda1, lambda2, ..., lambdaN]

各ラムダは、前のラムダから引数を受け取ります。

def let(*funcs):
    def wrap(args):
        result = args                                                                                                                                                                                                                         
        for func in funcs:
            if not isinstance(result, tuple):
                result = (result,)
            result = func(*result)
        return result
    return wrap

このメソッドでは、少しlisp / schemeのようなものを記述できます。

したがって、次のように書くことができます。

let(lambda x, y: x+y)((1, 2))

斜辺を計算するために、より複雑な方法を使用できます

lst = [(1,2), (2,3)]
result = map(let(
  lambda x, y: (x**2, y**2),
  lambda x, y: (x + y) ** (1/2)
), lst)

これはスカラー数のリストを返すため、複数の値を1つに減らすために使用できます。

多くのラムダを使用することは確かに効率的ではありませんが、制約がある場合は、何かをすばやく実行し、後で実際の関数として書き直すのに良い方法です。


-3

ラムダ関数は、関数の最も単純な形式として、1行で記述されることになっているため、 an entrance, then return


1
いいえ、イベント駆動型プログラムを作成する人なら誰でも、複数行のラムダが重要であると言うでしょう。
アカンカ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.