条件文の前の代入を取り除く

Pythonでは条件節の中に代入文を書くことができない。この制限は初心者が「代入と値が同じかどうかの比較を間違える」というありがちなミスをしてバグの原因になるのを防いでいる。が、しかしそのせいでif文の前で一時的な変数に値を代入しなければいけないのはちょっとめんどくさい。

import re

data = "aaaabbbbaaaa"

m = re.search("b+", data)
if m:
    print "'b+' is found at", m.start()

一つの解決法はスタックを導入することだ。僕のbigstackライブラリを使えば下のように書ける。

import re
import bigstack

data = "aaaabbbbaaaa"

if push(re.search("b+", data)):
    print "'b+' is found at", pop().start()

このライブラリはビルトイン名前空間にpushとpopという2つの関数を導入する。この関数はみなさんの想像通り、スタックのプッシュとポップを行う。このコードではmっていう変数に一時的に値を代入する必要が無くなっているのがわかると思う。

bigstackライブラリのコードは下記。すごくシンプルな実装だ。これですべての問題が解決するとは思わないが、これを使えばコードがきれいに書けるケースもあるだろう。特にwhile文などで。

"""bigstack.py: singleton stack to eliminate temporary variables"""

import __builtin__

BIG_STACK = []

def push(x):
    BIG_STACK.append(x)
    return x

def pop():
    return BIG_STACK.pop()

__builtin__.__dict__.update(
    push=push,
    pop=pop
)

You can see the original version here: NISHIO Hirokazu's blog: [Python]Eliminate assignment before conditional statement

        • -

id:odz 条件が満たされない場合に pop してないけどいいんかな

あ、そうか。popするだけのelse節を書くのは面倒だし、書かないとメモリ消費量がどんどん増えていくのか…。

>>> class Stash(object):
	def __call__(self, **kw):
		self.__dict__.update(kw)
		return kw.values()[0]

	
>>> stash = Stash()
>>> 
>>> if stash(x = 1):
	print stash.x

	
1

こういうのもあるけど、これはstashをbuiltinに入れてしまったら予期しないところで書き換えられて気持ち悪いバグを生み出しそうだし、いちいちstashオブジェクトを作るのは面倒だよなぁ。

うーん、いまいちだなぁ。一つ確実な方法はあって、それはバイトコードの分岐の前にDUP_TOPとSTOREをつっこんでしまうという力業なんだが…一時変数を取り除くためだけにそんな力業使いたくないよねぇ。