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

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

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

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

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

条件文の前の代入を取り除く bigstack (西尾泰和のはてなダイアリー)

先行事例。 代入とはちょっと違いますが。


まずは、ちゃんと動くやりかた。

—-
どうしても式中で代入したい!という貴兄は下記のような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
>>>

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

比較演算子の再定義

詳しい解説は省きますが、Pythonでは、変数に格納された数値を直接変更することが出来ません。

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

参考

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

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

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とか普通のメソッド名にしたほうが無難です。(多分)

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


次にダメだった方法

【それムリ】 +=を使ってみる

x+=a は 
x=x+a と同じ意味となります。

だったら、

x+= (-x+a)は、
x=aと同じ意味

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


#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 '+='

こんなエラーが出ました

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

>>=で遅延評価

なんてことをするとHaskellっぽいかも、というのが、今日のオチ、でした。自分では出来ませんが、、、

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


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

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

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)
Tags:

Related posts

Tags:

Comments are closed.