web-dev-qa-db-ja.com

Pythonの `lambda`式で割り当てが許可されないのはなぜですか?

これは Pythonのラムダ式内の代入 の複製ではありません。つまり、私はnotを使用して、Pythonをlambda式に代入する方法を尋ねています。

Λ計算の背景があります。次のコードを考えると、Pythonはlambda式で副作用を実行しても構わないと思っています。

#!/usr/bin/python

def applyTo42(f):
    return f(42)

def double(x):
    return x * 2

class ContainsVal:
    def __init__(self, v):
        self.v = v

    def store(self, v):
        self.v = v

def main():

    print('== functional, no side effects')

    print('-- print the double of 42')
    print(applyTo42(double))

    print('-- print 1000 more than 42')
    print(applyTo42(lambda x: x + 1000))

    print('-- print c\'s value instead of 42')
    c = ContainsVal(23)
    print(applyTo42(lambda x: c.v))


    print('== not functional, side effects')

    print('-- perform IO on 42')
    applyTo42(lambda x: print(x))

    print('-- set c\'s value to 42')
    print(c.v)
    applyTo42(lambda x: c.store(x))
    print(c.v)

    #print('== illegal, but why?')
    #print(applyTo42(lambda x: c.v = 99))

if __name__ == '__main__':
    main()

しかし、行をコメント解除すると

    print('== illegal, but why?')
    print(applyTo42(lambda x: c.v = 99))

あげる

SyntaxError: lambda cannot contain assignment

何故なの? これの背後にあるより深い理由は何ですか?

  • コードが示すように、それは機能的な意味での「純粋」ではありません。

  • 私が想像できる唯一の説明は、アサインメントがNoneでさえ、何も返さないということです。しかし、それは不自然に聞こえ、修正は簡単です(1つの方法:ボディがステートメントの場合、ラムダ式がNoneを返すようにします)。

答えではない:

  • それがそのように定義されているからです(なぜそのように定義されているのかを知りたいのです)。

  • それは文法にあるからです(上記を参照)。

  • ステートメントが必要な場合は、defを使用します(ステートメントを関数に入れる方法は尋ねていません)。

「構文/言語/セマンティクスを変更する」は、そのような変更の例を思い付くことができれば、それがなぜ悪いのかについての回答としては問題ありません。

16
stefan

lambdaが存在する完全な理由は、それが式であることです。1 lambdaに似ているがステートメントであるものが必要な場合、それはdefです。

Python式にステートメントを含めることはできません。これは実際、言語の基本であり、Pythonはその決定から多くのメリットを得ます。それがフロー制御のインデントが他の多くの試みのように不格好ではなく機能する理由ですCoffeeScriptのようなものです。これは、各行の最初のオブジェクトをざっと読むことで状態の変化を読み取​​ることができる理由です。コンパイラと人間の両方の読み手にとって、言語が解析しやすい理由の一部ですらあります。2

Pythonを変更して、ステートメント式の除算を「エスケープ」する方法をいくつか用意します。ただし、非常に注意深く限定された方法を除いて、それを完全に異なる言語に変更し、もはや持っていなかった言語に変更します。人々が最初にPythonを選択することを引き起こす多くの利点。

Pythonを変更してほとんどのステートメント式(Rubyなど)を作成する)を変更すると、Pythonの現在の利点なしに完全に異なる言語に再び変換されます。

そしてPythondidがこれらの変更のいずれかを行った場合、lambdaそもそも;2,3 式の中でdefステートメントを使用するだけです。


Pythonを代わりに割り当て式に変更することについてはどうでしょうか?さて、「各行の最初のオブジェクトをスキミングすることで状態の変更を読み取ることができる」ということは明らかです。Guidoは通常、 _if spam=eggs_は、有用なものよりもエラーであることが多いという事実について。

Pythonがsetattrのように、またはglobals()で明示的に___setitem___を呼び出すなど、必要なときにそれを回避する方法を提供するという事実は、直接的な構文サポートが必要なことを意味するわけではありません。非常にまれにしか必要とされないものは、構文糖に値するものではありません。さらに、実際に行われたときに眉や赤旗を発生させるほど珍しいものについてはなおさらです。


1. Python 1.0で最初にlambdaを追加したとき、それがGuidoの理解であったかどうかはわかりません。しかし、それがlambdaが削除されなかった理由です。 Python 3.0。

2.実際、Guidoは何度も、人間が頭の中で実行できるLL(1)パーサーを許可することは、言語がステートメントベースであることの十分な理由であり、他の利点でさえ、議論される。 数年前にこれについて書いた 誰かが興味を持っているなら。

3.なぜそれほど多くの言語dolambdaを持っているにもかかわらずdef式を持っているのか疑問に思っている場合:In C++からRubyに至るまでの多くの言語では、関数は渡せる一流のオブジェクトではないため、一流であるが関数のように機能する2番目のものを発明する必要がありました。 SmalltalkからJavaに至るまで、関数は存在せずメソッドのみであるため、メソッドではない2番目のものを発明する必要がありました。 1つのように動作します。 Pythonにはこれらの問題はありません。

4. C#やJavaScriptなどのいくつかの言語は、実際には完全に機能するインライン関数定義を備えていましたが、より簡潔でボイラープレートを少なくするために、ある種のlambda構文を純粋な構文糖として追加しました。これは実際にはPython=で行う価値があります(ただし、これまでのところ、優れた構文でのすべての試みはうまくいっていません))が、現在のlambda構文ではありません。 defとほぼ同じくらい詳細です。

15
abarnert

syntaxの問題があります。割り当てはstatementであり、ラムダの本体はexpressionsしか持てません。 Pythonの構文はこのように設計されています1https://docs.python.org/3/reference/grammar.html で確認してください。

セマンティクス問題もあります:各ステートメントは何を返しますか

ラムダは非常にシンプルで短いコードを意図しているので、これを変更することに関心はないと思います。さらに、ステートメントはsequencesのステートメントも許可し、ラムダには望ましくありません。

ラムダ本体で特定のステートメントを選択的に許可し、セマンティクスを指定することで修正することもできます(たとえば、割り当てはNoneを返すか、割り当てられた値を返します。後者は私にはより意味があります)。しかし、メリットは何ですか?

ラムダと関数は交換可能です。ラムダの本文の特定のステートメントのユースケースが本当にある場合は、それを実行する関数を定義して、特定の問題を解決できます。


多分あなたは MacroPy でそれを可能にする構文マクロを作成することができます(私はプロジェクトのファンなので、私はただ推測しているだけですが、まだそれに飛び込む時間はありませんでした) 。

たとえば、MacroPyでは、f[_ * _]lambda a, b: a * bに変換するマクロを定義できるため、定義した関数を呼び出すラムダの構文を定義することは不可能ではないはずです。


1 ラムダは式が可能な場所にある可能性があるため、変更しないことの良い理由は、構文が損なわれることです。そしてステートメントはすべきではありません。しかし、それは私自身の非常に主観的な発言です。

6
fferri

私の回答は上記の chepnerのコメント に基づいており、他の信頼できる、または公式の情報源からのものではありませんが、役に立つと思います。

ラムダ式で代入が許可されている場合、混乱を招くエラー==(同等性テスト)と=(割り当て)は、野生に逃げる可能性が高くなります。

例:

>>> # Correct use of equality test
... list(filter(lambda x: x==1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[1, 1.0, (1+0j)]

>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is always None
... list(filter(lambda x: None, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[]

>>> # Suppose that assignment is used by mistake instead of equality testing
... # and the return value of an assignment expression is the assigned value
... list(filter(lambda x: 1, [0, 1, 0.0, 1.0, 0+0j, 1+0j]))
[0, 1, 0.0, 1.0, 0j, (1+0j)]
2
Leon

lambda内でexec()(およびeval())が許可されている限り、lambda内で代入を行うことができます。

q = 3

def assign(var_str, val_str):
    exec("global " + var_str + "; " + 
    var_str + " = " + val_str)

lambda_assign = lambda var_str, val_str: assign(var_str, val_str)

q ## gives: 3

lambda_assign("q", "100")

q ## gives: 100

## what would such expression be a win over the direct:

q = 100

## ? `lambda_assign("q", "100")` will be for sure slower than
##   `q = 100` isn't it?

q_assign = lambda v: assign("q", v)

q_assign("33")

q ## 33

## but do I need lambda for q_assign?

def q_assign(v): assign("q", v) 

## would do it, too, isn't it?

ただし、ラムダ式では本体内で1つの式のみを定義できるため(少なくともPython ...))、ラムダ内での割り当てを許可するポイントは何でしょうか?その実質的な効果は、直接割り当てることです(ラムダを使用せずに)q = 100、そうではありませんか?

少なくとも1つの関数のルックアップと実行の実行が少なくて済むため、定義されたラムダに対して実行するよりもさらに高速になります...

2
Gwang-Jin Kim

これ以上深い理由はありません。ラムダや関数型言語の設計とは何の関係もありません。プログラマーが=演算子と==演算子を混在させないようにするだけです。これは他の言語でよくある間違いです

python bdfl GVRがラムダやその他の機能的機能に対する愛情のない側面を表現し、python 3完全に https://www.artima.com/weblogs/viewpost.jsp?thread=98196

この記事を執筆している時点で、制限付きの名前バインディング式の割り当てを含めるかどうかについて、コア開発者が最近白熱した議論をしているのが見られました。

あなたがそれをあなた自身が言ったように、それは間違いなく副作用や純粋さについてではありません、彼らはラムダが単一の表現以上になることを望んでいないだけです... ... ...

そうは言っても、ここではラムダでの複数式の割り当てについて説明します。興味がある場合は読んでください。

Pythonでは不可能ではありません。実際、kwargs(キーワード引数)を使用して(ab)変数と遅延バインディングを回避する必要がある場合がありました。

編集:

コード例

f = lambda x,a=1: (lambda c = a+2, b = a+1: (lambda e = x,d = c+1: print(a,b,c,d,e))())()

f("w")

# output 1 2 3 4 w

# expression assignment through an object's method call

if let(a=1) .a > 0 and let(b=let.a+1) .b != 1 and let(c=let.b+let.a) .c:
    print(let.a, let.b, let.c)

# output 1 2 3
1
guramarx

フェロー全員がすでにこれに答えたと思います。次のような場合は、主にラムダ関数を使用します。

-特定の場所で完全に機能するいくつかの単純な関数を作成します(ほとんどの場合、他のいくつかの大きな関数の中に隠されています-ラムダ関数には名前がありません-マップ、リストなどの他の組み込み関数で使用できますなど...

>>> Celsius = [39.2, 36.5, 37.3, 37.8] 
>>> Fahrenheit = map(lambda x: (float(9)/5)*x + 32, Celsius) # mapping the list here  
>>> print Fahrenheit
[102.56, 97.700000000000003, 99.140000000000001, 100.03999999999999]

このウェブページにアクセスしてください、これは役に立つかもしれません。 https://www.python-course.eu/lambda.php

0
Jonas Amara

現状では、Pythonはステートメントベースの言語として設計されています。したがって、代入やその他の名前バインディングはステートメントであり、結果はありません。

Pythonコア開発者は現在 name-binding expression を導入するPEP 572について議論しています。

0
holdenweb