Pythonが複数行のラムダを許可しないのはなぜですか?


50

BDFLがPythonラムダを単一行にすることを選択した具体的な理由を誰かが説明できますか?

これはいい:

lambda x: x**x

これによりエラーが発生します。

lambda x:
    x**x

ラムダを複数行にすると、通常のインデント規則が何らかの形で「乱れ」、例外を追加する必要があることを理解していますが、それだけのメリットはありませんか?

たとえば、JavaScriptを見てください。これらの匿名関数なしでどのように生きることができますか?彼らは不可欠です。Pythonistasは、引数として渡すためにすべての複数行関数に名前を付ける必要をなくしたくないですか?


3
Guidoが複数式のラムダを許可しない具体的な理由に注意し、それらを却下することを考慮して、実際の答えではなく検証を求めていると仮定します。
ジェイソンベイカー

3
7文字を保存する以外に、これはどのように優れていdefますか?現在、まったく同じ視覚構造になっています。
11

回答:


42

グイド・ファン・ヴァン・ロッサムは自分で答えました。

しかし、このようなソリューションには、多くの場合「Pythonicity」がありません。これは、Pythonの優れた機能の捉えにくい特徴です。Pythonicityをハードな制約として表現することは不可能です。PythonのZenでさえ、Pythonicityの単純なテストに変換されません...

上記の例では、提案されたソリューションのアキレス腱を見つけるのは簡単です:二重コロンは、実際には構文的に明確ですが(「パズル制約」の1つ)、完全にarbitrary意的であり、Pythonの他のものには似ていません...

しかし、最終的には(これが提出者を意図せずに誤解させることを認めるところであるため)式の途中にインデントベースのブロックを埋め込む解決策が受け入れられないことがわかったため、私もそれを拒否しています。ステートメントのグループ化(ブレースや開始/終了キーワードなど)の代替構文も同様に受け入れられないと思うので、これにより、複数行のラムダは解決不可能なパズルになります。

http://www.artima.com/weblogs/viewpost.jsp?thread=147358

基本的に、彼は、解決策は可能ですが、Pythonがどのようなものであるかについては一致しないと言います。


2
+1スレッドリンクに感謝します-しかし、私は複数行のラムダに感謝します-それらは非常に貴重です-JavaScriptを見て、PHPにも含まれています。
ツリーコーダー

2
@greengitネストされたdefを使用できます。無名関数とは異なりますが、十分に近いものです。
-jsternberg

2
関数を引数として渡すときにネストされたdefが役に立たない
それが

1
@greengit-ここにコメントを投稿するよりも、GvRでこれを取り上げた方が良いと思います。
ジェイソンベイカー

9
@greengit:関数を別の関数の引数として渡すことができることを知っていますか?インラインで書くことはできませんが、利用できないプログラミング手法はありません。
-btilly

24

Pythonで複数行のラムダを実行するのは完全に問題ありません:

>>> f = lambda x: (
...   x**x)
>>> f
<function <lambda> at 0x7f95d8f85488>
>>> f(3)
27

実際のラムダ制限は、ラムダが単一の式でなければならないという事実です。キーワードを含めることはできません(python2 printreturn)。

GvRは、通常はパラメーターとして使用されるため、ラムダのサイズを制限するためにそうすることを選択します。実際の機能が必要な場合は、使用しますdef


1
複数は '\ n'文字の挿入に関するものです:D pythonにはマルチステートメントラムダがありません。あなたは本当に使いたいですdef。考えてみてください。関数のパラメーターとしてcallableが本当に必要ですか?そして、その関数のユーザーが通過を許可されていない、あなたのデフォルトの呼び出し可能なの?あなたが彼らにそれを与えないなら、彼らはどうやってそれを渡すことができますか?
ヴィートデトゥリオ

ところで、匿名機能の必要性の例を提供できますか?
ヴィートデトゥリオ

1
ええ、私は単一の表現の制限が本当に苛立たしいと感じています。確かに、複数式のラムダを許可すれば、人々は間違いなくそれを乱用し始めますが、逆はあまりにも制限的なオムホです。
-rbaleksandar

10

私はこれが非常に古いことを知っていますが、ここに参考として置きます。

ラムダを使用する代わりdefに、従来とは異なる方法でa を使用することもできます。目標は、a defを関数に渡すことです。これは、デコレータという1つの状況でのみ実行できます。この実装でdef resultは関数は作成されず、の結果が作成され、最終的にはreduce()になりますdict

恥知らずなプラグ:私はここでこれの多くをします

>>> xs = [('a', 1), ('b', 2), ('a', 3), ('b', 4)]
>>> foldl = lambda xs, initial: lambda f: reduce(f, xs, initial)
>>> @foldl(xs, {})
... def result(acc, (k, v)):
...     acc.setdefault(k, 0)
...     acc[k] += v
...     return acc
...
>>> result
{'a': 4, 'b': 6} 

複数ステートメントのラムダを実行できることに注意してください。ただし、実際には非常にいコードが必要です。ただし、興味深いのは、この実装でスコープがどのように機能するかです(name変数の複数の使用法と変数のシャドウイングに注意してくださいmessage

>>> from __future__ import print_function
>>> bind = lambda x, f=(lambda x: x): f(x)
>>> main = lambda: bind(
...     print('Enter your name.'), lambda _: bind(
...     raw_input('> '), lambda name: bind(
...     'Hello {}!'.format(name), lambda message: bind(
...     print(message), lambda _: bind(
...     'Bye {}!'.format(name), lambda message: bind(
...     print(message)
... ))))))
>>> main()
Enter your name.
> foo
Hello foo!
Bye foo!

モナドアプローチの+1
jozefg

モナドは、thenables、future / promise、またはJavaScript BTWのコールバックとも呼ばれます。
aoeu256

3

マルチステートメントのラムダを一緒にハックすることは、パイロスペードが行うほど悪くはありません:Haskellのようにバインドを使用してモナド関数の束を構成できることは確かですが、Pythonの不純な世界にいるので、副作用を使用して同じことを達成します。

ブログでこれを行う方法をいくつか説明します。

たとえば、Pythonはタプルの要素を順番に評価することを保証しているため、,命令型のように使用できます;。などの多くのステートメントを、などのprint式で置き換えることができsys.stdout.writeます。

したがって、以下は同等です。

def print_in_tag_def(tag, text):
    print "<" + tag + ">"
    print text
    print "</" + tag + ">"

import sys
print_ = sys.stdout.write
print_in_tag_lambda = lambda tag, text: (print_("<" + tag + ">"),
                                         print_(text),
                                         print_("</" + tag + ">"),
                                         None)[-1]

None最後にa を追加し、[-1]; を使用して抽出したことに注意してください。これにより、戻り値が明示的に設定されます。これを行う必要はありませんが、それなしではファンキーな(None, None, None)戻り値を取得できますが、これは気にすることも気にしないこともあります。

したがって、IOアクションをシーケンスできます。ローカル変数はどうですか?

Python =はステートメントを形成するため、同等の式を見つける必要があります。1つの方法は、引数として渡されたデータ構造の内容を変更することです。例えば:

def stateful_def():
    foo = 10
    bar = foo * foo
    foo = 2
    return foo + bar

stateful_lambda = (lambda state: lambda *_: (state.setdefault('foo', 10),
                                             state.setdefault('bar', state.get('foo') * state.get('foo')),
                                             state.pop('foo'),
                                             state.setdefault('foo', 2),
                                             state.get('foo') + state.get('bar'))[-1])({})

で使用されているいくつかのトリックがありますstateful_lambda

  • *_引数は、私たちのラムダを取ることができます任意の引数の数を。これにより引数がゼロになるため、の呼び出し規約を回復しstateful_defます。
    • 引数を呼び出すこと_は、「この変数を使用するつもりはない」という規則にすぎません。
  • 別の(「メイン」)関数を返す1つの(「ラッパー」)関数があります。 lambda state: lambda *_: ...
    • lexical scopeのおかげで、最初の関数の引数は2番目の関数のスコープ内になります
    • ここでいくつかの引数を受け入れ、後で残りを受け入れるために別の関数を返すことをカリー化と呼びます
  • すぐに「ラッパー」関数を呼び出して、空の辞書を渡します。 (lambda state: ...)({})
    • これは、私たちは、変数に割り当てることができますstate値を{}代入文を使用せずに(例えば。state = {}
  • キーと値stateを変数名とバインド値として 扱います
    • これは、すぐに呼び出されるラムダを使用するよりも面倒ではありません
    • これにより、変数の値を変更できます
    • state.setdefault(a, b)代わりにa = bstate.get(a)代わりにa
  • 前のように、タプルを使用して副作用を連結します
  • ステートメントの[-1]ように機能する最後の値を抽出するために使用しますreturn

もちろん、これはかなり面倒ですが、ヘルパー関数を使用してより良いAPIを作成できます。

# Keeps arguments and values close together for immediately-called functions
callWith = lambda x, f: f(x)

# Returns the `get` and `setdefault` methods of a new dictionary
mkEnv = lambda *_: callWith({},
                            lambda d: (d.get,
                                       lambda k, v: (d.pop(k), d.setdefault(k, v))))

# A helper for providing a function with a fresh `get` and `setdefault`
inEnv = lambda f: callWith(mkEnv(), f)

# Delays the execution of a function
delay = lambda f x: lambda *_: f(x)

# Uses `get` and `set`(default) to mutate values
stateful_lambda = delay(inEnv, lambda get, set: (set('foo', 10),
                                                 set('bar', get('foo') * get('foo')),
                                                 set('foo', 2),
                                                 get('foo') + get('bar'))[-1])

あなたは冗談され、この悪夢のようになります笑
アレクサンダー・ミルズ

1
@AlexanderMills Heh、これは実世界の例ではなく物事がそれほど悪くないことを示すために、パイロスペードのラムダ-イン-ラムダ-イン-ラムダアプローチの反論を意図したものではありません。実際には、これは更に多く、今我々が持っていることを簡単にすることができpython.org/dev/peps/pep-0572
Warbo

1

私は貢献できましたが、ラインブレーカーを使用します:

x = lambda x,y: x-y if x<y \ 
                     else y-x if y<x \
                     else 0

例のように、Pythonがonelinersを作成できるという非常に素晴らしいことを忘れないでください。

a=b=0; c=b+a; d = a+b**2 #etc etc

そして、ラムダは非常に強力ですが、1つの関数全体を置き換えることを意図したものではありません。

makeTag = lambda tagName: "<{}>".format(tagName)
closeTag = lambda tagName: makeTag("/"+str(tagName))
openTag = lambda tagName: makeTag(tagName)
writeHMTLline = lambda tag,content: ""+opetTag(tag)+str(content)+closeTag(tag)

しかし、あなたは本当にこのようにしたいですか?しばらく経つとほとんど読めなくなり、解き明かされていないロープから始まります。 解かれたロープ

ラムダは、マップ、フィルター、および機能指向プログラミング(特に)のリデュース関数で、1つだけの関数として記述されています。たとえば、整数で2で割り切れる値の文字値を取得する

chrDev2 = lambda INT: chr(INT) if isinstance(INT,int) and INT%2==0 else INT
someStringList = map( chrDev2, range(30) )
>>> ['\x00', 1, '\x02', 3, '\x04', 5, '\x06', 7, '\x08', 9, '\n', 11, '\x0c', 13, '\x0e', 15, '\x10', 17, '\x12', 19, '\x14', 21, '\x16', 23, '\x18', 25, '\x1a', 27, '\x1c', 29]

複雑な関数(または複数のlambdasを定義し、別のlambda内に配置することにより、関数式関数として使用できます。

def someAnon(*args): return sum(list(args))
defAnon = lambda list: [ x*someAnon(*list) for x in list]

しかし、Pythonには別の方法で関数式のサポートsuperAwesomeFunctionがあります。

SAF = superAwesomeFunction # there is no () at the end, 

したがって、SAFを呼び出すときは、superAwesomeFunctionまたはメソッドを呼び出します。Libフォルダーを検索すると、ほとんどのpython __builtin__モジュールがそのように書かれていることがわかります。これは、ユーザーが使用するのに十分ではないが、いくつかの機能に必要な特定のタスクを実行する機能が必要になる場合があるためです。したがって、「superAwesomeFunction」という名前の2つの関数を使用することはできず、「superAwesomeFunctionDoingBasicStuf」および「realSuperAwesomeFunction」を選択し、「superAwesomeFunction」変数に「realSuperAwesomeFunction」を入れるだけで済みます。

console importedModule.__file__(実際の例import os;os.__file__)を入力することでインポートされたモジュールの場所を見つけることができ、importedModule.pyと呼ばれるファイルにそのディレクトリをたどってエディターでそれを開き、独自の「知識」を最大化する方法を見つけます。

これがあなたや他の同僚のトラブルに役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.