pythonワンライナーを書く上で障害になるのは、代入文の存在である。
代入は関数ではなく文なので、素直に書くと一行を消費してしまうし、lambdaやコレクション型の中にも入れられない。
よく知られた対策としては、グローバル変数テーブルを直接書き換えるという大技がある。
>>> globals().__setitem__("hoge", 1) >>> hoge 1
でも__setitem__とか呼ぶのは、はっきり言ってキモい。それに、ローカル変数は書き換えられないという問題もある。もっと普通のpythonの枠内でpythonワンライナーを書きたい。
長らくこの問題に悩んでいたが、ついに等価の表現を発見した。リストならアンダーバー付きのメソッドを呼ばなくても行ける。
>>> l = [1] >>> (l.pop(0), l.append(2))[-1] >>> l [2]
ワンライナーとして変数辞書と同様に使える表現を考えてみる。lispのalistを参考にして作る。
(lambda setalist, getalist, alist: # 環境のalistとアクセス用関数を定義(lispのletと同様) ((setalist("hoge", 1, alist), # 以下main print(getalist("hoge", alist)), setalist("hoge", 2, alist), print(getalist("hoge", alist)), setalist("fuga", "fuga", alist), print(getalist("fuga", alist)), print(getalist("hoge", alist)), setalist("lst", [], alist), getalist("lst", alist).extend([1,2,3,4,5]), print(getalist("lst", alist)[0:2]) # mainここまで ,)))( # ここから一番外のlambdaの引数 (lambda key, value, alist: # setalistの実体 (lambda search_result: ((alist.pop(search_result[0]) if search_result != [] else None), alist.append((key, value)), value)[-1])([i for (i, (k,v)) in enumerate(alist) if k==key])), (lambda key, alist: # getalistの実体 (lambda tmp:tmp[0] if tmp != [] else None)( [e[1] for e in alist if key == e[0]])), [] # alistの実体 )
出力
1 2 fuga 2 [1, 2]
ほぼ手続き型ライクに使える。ただし、リストの要素などには直接代入できないので、そういったことに気を配る必要があるが(リストの要素に代入するだけならinsertしてpopすれば良いので関数で書けそう)。
もう少し頑張れば、可読性の高い(アンダーバー付きのメソッドを呼ばなくて良い)pythonワンライナーを書けそうだ。