💨

pythonのlambdaは式構文だから命令文を書けないとか言われるけどふつーの関数が無名関数の特徴をもつから別に困らない

2023/09/26に公開

pythonは、式構文と命令文が明確に分かれており、lamdaが式扱いになる。これはpythonの問題点だとは言われる。

公式ドキュメントにもその通りと書いてある。あえてpython2.7にリンク。後半を抜粋する。

https://docs.python.org/ja/2.7/reference/expressions.html#lambda

パラメータの構文の一覧は 関数定義 を参照してください。ラムダ式で作成された関数は命令文を含むことができない点に注意してください。

しょっちゅうdisられる

lambdaの中で命令文が書けないのでこれが他言語育ちからやたらdisられる。
なんなら、rubyの原作者のまつもとゆきひろ氏にすらdisられる。

https://jp.quora.com/Ruby-ni-deki-te-Python-ni-deki-nai-koto-ha-nani-desu-ka/answers/142958781

滅したいとまで言われる

おあつらえ向きに最近またバズった記事がある。記事自体は2023の投稿だが、なぜだか2024/5辺りに時間差でバズってた。

https://dev.thanaism.com/2023/05/python-sucks/

言葉の節々に表れてるのだが、彼はjavascriptが至高だと思ってる節がある。
無名関数が大好きで、複雑な無名関数を書くことに喜びを感じているのだ。

三項演算子は無名関数御用達といえよう。pythonにおいての類似機能はifを後置する"条件式"しかない。

彼にとっては、「無名関数が気持ち良く書けない!メンタルモデルに一致しない!キモい!」と仰るわけだ。

彼がほんとうはどう思ってるかなぞ知らない。だが、そう解釈すれば彼の主張の全てが腑にはおちる。

そもそもpython3におけるlambdaとは

だが待ってほしい。lambdaの章には前半にもうひとつ、英語から未翻訳の但し書きがある。

Lambda expressions (sometimes called lambda forms) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression lambda parameters: expression yields a function object. The unnamed object behaves like a function object defined with

端的には「式構文の中で、関数定義を短く書くやりかただよ」と言ってる。

根本的に誤解されているのだが、pythonでは、他の言語でいうところの”関数”の”定義”は存在しない。

これはドキュメントにも明記されている。これまたあえてpython2.7にリンク。主題がぼやけるので一部だけ抜粋する。

https://docs.python.org/ja/2.7/reference/compound_stmts.html#function-definitions

関数定義は実行可能な文です。関数定義を実行すると、現在のローカルな名前空間内で関数名を関数オブジェクト (関数の実行可能コードをくるむラッパ) に束縛します。

「関数定義を実行する」
どこかで聞いた話ではあるまいか?

lambdaとdefの違い

実はpythonでの関数定義は、無名関数の定義とほぼ挙動が変わらない。 言うなれば”有”名関数の作成であるとも言える。

軽量言語としては、デコレータを実装してる数少ない言語であるのがその証拠である。

デコレータでの応用例

適当なデコレータがあったほうが良いだろう。
ここではサンプルなのでテキトーなほど望ましい。
chatgpt-4に考えてもらった。
処理そのものは任意長さの引数を勝手に2倍するだけの完全なダミーである。

def double_args(func):
    def wrapper(*args, **kwargs):
        new_args = [x * 2 for x in args]
        return func(*new_args, **kwargs)
    return wrapper

@double_args
def multiply(a, b):
    return a * b

@double_args
def multiply2(a, b):
    return a + b

defの中にdefがある。これがデコレータの御作法であり、pythonの特徴である。
例ではmultipy()の入力に干渉し、ただ2倍するだけのデコレータである。

だが、double_argsをmultiply2につけても、それぞれ期待通りに動作する。
double_argsの”定義”は一回しかしてないのにだ。

ここで「関数定義を実行する」というニュアンスを思い出してほしい。

double_argsデコレータを参照/実行するたびに、内部のwapper()は、引数のfuncを埋め込まれて毎回作り直されているのだ。
これはpython特有の他言語に類を見ない、かなり画期的な挙動であると思われる。

つまり無理にlamda構文を使わなくても、ふつーにdefで関数を書けば期待通りに動作するのである。
であるからして、小生はlamdaで命令が使えなくてもまるで困ってない。そのつど関数を作れば良いからだ。

引数で渡す

似て非なる話をもう1点。

sort() keyパラメタ、多くの人はlambdaを渡さなければならないと思ってやしないだろうか?
そんなことはない。

def custom_sort(value):
    return len(value)

words = ["apple", "banana", "cherry", "date"]
words.sort(key=custom_sort)

print(words)  # 出力: ['date', 'apple', 'banana', 'cherry']

この場合は key=len でもいけるので大した意味はない。だが可能なのはわかってもらえるだろうか。

逆説的に、pythonのlamdaは「どうしても1行で書きたいひとのための関数定義のサブセット」と観るべきだろう。
機能が少ないのは必然だ。むしろlambdaで複雑な処理を書こうとするほうがおかしいとさえ言える。

ここまで来ればあとは”趣味”の問題だ。

無名関数大好きな方々は、「1行で書かなければならない」と思い込みすぎでは?

Discussion