【Py】 式の中で変数に値を代入する101番目の方法(?)

[pukiwiki]
Pythonでは、リスト内包表記やラムダ(lambda)による関数定義では、式のみを使えます。

なので、「=(イコール、等号)」を使った代入は使えません。

関数型言語でも、Mathematicaでは式の中で代入できるのに~ ちょっと不便。ということで、この制限を回避できないか試した記録です 。

[/pukiwiki]

[pukiwiki]
「せっかくPythonは数値や文字列がイミュータブルなのに」 というツッコミは無しの方向で(汗

*[[条件文の前の代入を取り除く bigstack (西尾泰和のはてなダイアリー):http://d.hatena.ne.jp/nishiohirokazu/20080214/1202981087]]
先行事例。 代入とはちょっと違いますが。

—-
まずは、ちゃんと動くやりかた。
[/pukiwiki]

—-
どうしても式中で代入したい!という貴兄は下記のようなlet関数を定義します。おしまい

def let ( key,value,dict):
  dict.update( {key:value})
  return value

使い方 その1 リスト内包表記

>>> [let("x",i,locals()) for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x
9 #xに9が代入されてる

使い方 その2 lambda

>>> f=lambda y,d=locals():let("x",y,d)
>>> f(101)
101
>>> x
101
>>> 

実際は、あまり使いませんが。

[pukiwiki]
*比較演算子の再定義
詳しい解説は省きますが、Pythonでは、変数に格納された数値を直接変更することが出来ません。
-[[ググる:Python イミュータブル]]

なので、「値を保持するためのクラス」を作成します
Pythonでは、アンダースコア二つでくくられた特殊な名前のメソッド(特殊メソッド)により、演算子の動作を再定義できます。

参考
-[[operatorモジュール — 関数形式の標準演算子:http://www.python.jp/doc/release/lib/module-operator.html#l2h-1086]]
-[[Py2.5日本語ドキュメント索引 アンダースコア:http://www.python.jp/doc/2.5/lib/genindex.html#letter-_]]

式中でも使える演算子を一つ潰して、代入に使えるようにしたオブジェクトを作ってみます。

今回は、左シフト演算子(<<) をオーバーロードしました。 
[/pukiwiki]

class Var(object):
    def __init__(self,value=0):
        self.value=value
    def __lshift__(self,y):
        self.value=y
        return y

実行例

>>> x<<100
100
>>> x.value
100
>>> [x<<i  for i in range(10)]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> x.value
9
>>> 

あんましうれしくない。。。
上記let関数と違ってオブジェクトなので、スコープを気にしなくてもいいのがメリット、かな?

今改めて考えると、値を保持するだけなら、特殊メソッド名使わずとも、 setとか普通のメソッド名にしたほうが無難です。(多分)

めんどくさいですが、ちゃんと四則演算全てを実装してやることも可能かと。

[pukiwiki]
—-
次にダメだった方法
*【それムリ】 +=を使ってみる
x+=a は 
x=x+a と同じ意味となります。

だったら、
x+= (-x+a)は、
x=aと同じ意味

になるはずです。やってみます。

[/pukiwiki]


#CPython2.5
>>> x=0
>>> [x+=(-x+i) for i in xrange(100)]
SyntaxError: invalid syntax

#IronPython2.6RC2
>>> x=0
>>> [x+=(-x+i) for i in xrange(100)]
  File "<stdin>", line 1
    [x+=(-x+i) for i in xrange(100)]

      ^
SyntaxError: unexpected token '+='

こんなエラーが出ました

+=も使えないのでした。代入を伴う演算子は全て使えないみたいです。残念。
等号を含む比較演算子は使えるみたいですけれども。

[pukiwiki]

*>>=で遅延評価
なんてことをすると[[Haskell>ググる:Haskell]]っぽいかも、というのが、今日のオチ、でした。自分では出来ませんが、、、

考えてみれば、Pythonで書かれた数式処理システム 「[[SymPy>ググる:SymPy]]」も、同じように演算子をオーバーロードして式の表現やSymbolクラスを実装してるはず、なので、また今度ヒマな時にソースを読んでみると面白いのかも 
(そこまで辿りつけない気もしますけれども)

—-
オマケ
上記Varクラスに、さらに四則演算を実装した例。
(シフト演算子は省いてあります)

exec文を使ってます。
これだと煩雑なので、全部、基底クラスに委譲する方法とか無いかしらん?
(有りそうな気もするのですが)
[/pukiwiki]

class Var(object):
    def __init__(self,value=0):
        self.value=value

    def _v(self,y):
        if isinstance(y,type(self)):
          return y.value
        else:
          return y
    def __lshift__(self,y):
        self.value=self._v(y)
        return self
    def __repr__(self):
        return str(self.value)

    oplist="eq,== gt,> lt,< ge,>= le,<="
    tmpl="""def __%s__(self,y):
        return self.value %s self._v(y)"""
    for i in oplist.split():
        t=tuple(i.split(","))

        exec (tmpl%t) 
    #
    oplist="add,+  sub,-  mul,*  div,/"
    tmpl="""def __%s__(self,y):
        return type(self)(self.value %s self._v(y))"""
    tmpl2="""def __r%s__(self,y):
        return type(self).spr(self._v(y) %s self.value)"""
    tmpl3="""def __i%s__(self,y):
        self.value=self.value %s self._v(y)
        return self"""
    tmpl4="""def __ri%s__(self,y):
        self.value= self._v(y) %s self.value
        return self"""
    oplist=oplist.split()
    for i in oplist:
        t=tuple(i.split(","))
        exec (tmpl%t) 
        exec (tmpl2%t) 
        exec (tmpl3%t)
        exec (tmpl4%t)



コメントを残す

メールアドレスが公開されることはありません。