静かなる名辞

pythonとプログラミングのこと



【python】改行せずに代入文と等価のことをする

 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ワンライナーを書けそうだ。