web-dev-qa-db-ja.com

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

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

これはいい:

lambda x: x**x

これはエラーになります:

lambda x:
    x**x

ラムダを複数行にすると、どういうわけか通常のインデントルールが「邪魔」され、さらに例外を追加する必要があることを理解していますが、それはメリットに値しませんか?

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

53
treecoder

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

しかし、そのようなソリューションには「Pythonicity」が欠けていることがよくあります。これは、優れたPython機能のとらえどころのない特性です。 Pythonicityをハード制約として表現することは不可能です。 Zen of Pythonでも、Pythonicityの単純なテストにはなりません...

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

しかし、私もそれを拒否します。なぜなら、結局(そしてこれが、私がサブミッターを誤って誤解させることを認めているところである)、式の途中にインデントベースのブロックを埋め込むという容認できない解決策を見つけたからです。ステートメントのグループ化(ブレースやbegin/endキーワードなど)の代替構文も同じように受け入れられないので、これは複数行のラムダを解決できないパズルにします。

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

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

46
BlackJack

pythonで複数行のラムダを実行するのはまったく問題ありません。

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

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

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

25
Vito De Tullio

これは非常に古いことを知っていますが、参照としてここに入力します。

ラムダを使用する代わりに、従来とは異なる方法でdefを使用することもできます。目的は、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!
_
10
pyrospade

複数ステートメントのラムダを一緒にハックすることは、pyrospadeが明らかにするほど悪いことではありません。私たちはcould Haskellのようにbindを使用して一連のモナド関数を作成しますが、不純な世界にいるためです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を追加し、_[-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_で使用されているいくつかのトリックがあります:

  • _*__引数により、ラムダはany引数の数を取ることができます。これはzero引数を許可するため、_stateful_def_。の呼び出し規約を回復します。
    • 引数___の呼び出しは、「この変数を使用しない」とだけ表記されています
  • 1つの(「ラッパー」)関数が別の(「メイン」)関数を返す:_lambda state: lambda *_: ..._
    • 字句スコープ のおかげで、最初の関数の引数は2番目の関数のスコープ内になります
    • ここでいくつかの引数を受け入れ、後で残りを受け入れるために別の関数を返すことは currying として知られています
  • 空の辞書を渡して、すぐに「ラッパー」関数を呼び出します:_(lambda state: ...)({})_
    • これにより、代入ステートメントを使用せずに変数stateを値_{}_に割り当てることができます(例:_state = {}_)
  • stateのキーと値を変数名とバインド値として扱います
    • これは、即座に呼び出されるラムダを使用するよりも面倒ではありません
    • これにより、変数の値を変更できます
    • _a = b_の代わりにstate.setdefault(a, b)を使用し、aの代わりにstate.get(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])
_
4
Warbo

私は貢献できるかもしれませんが、改行を使用します:

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

そして、lambdaは非常に強力ですが、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)

しかし、本当にこのようにしたいですか?それはしばらくするとほとんど読めなくなり、解き明かされた終わりから始まるロープの始まりに到達するようなものです。 unraveled rope

ラムダは、(特に)関数指向プログラミングのマップ、フィルター、およびリデュース関数で、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]

複雑な関数(またはそれ以上の数のラムダ)を定義し、それを別のラムダ内に配置することで、関数表現関数として使用できます。

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」を追加すれば完了です。

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

これがあなたやおそらく他の問題を抱えている同僚たちに役立つことを願っています。

1
Danilo