もう少し遊んだ後は、私はそれを持っていると思います。最初に質問したとき、演算子のオーバーロードについて知りませんでした。
では、このPythonセッションで何が起こっているのでしょうか。
>>> from sympy import *
>>> x = Symbol(x)
>>> x + x
2*x
インタプリタが式を評価する方法について特別なことは何もないことがわかりました。重要なことは、Pythonが翻訳することです
x + x
の中へ
x.__add__(x)
__add__(self, other)
Symbolは、を返すように定義されているBasicクラスを継承しAdd(self, other)
ます。(これらのクラスは、sympy.core.symbol、sympy.core.basic、およびsympy.core.addにあります(確認したい場合)。)
Jerubが言っていたように、関数を評価する前に、基本的に関数の2番目の引数をsympy式に変換するSymbol.__add__()
というデコレータがあります。その過程で、以前に見たものであるという関数が返されます。_sympifyit
__sympifyit_wrapper
オブジェクトを使用して操作を定義することは、かなり巧妙な概念です。独自の演算子と文字列表現を定義することにより、簡単な記号代数システムを非常に簡単に実装できます。
symbolic.py-
class Symbol(object):
def __init__(self, name):
self.name = name
def __add__(self, other):
return Add(self, other)
def __repr__(self):
return self.name
class Add(object):
def __init__(self, left, right):
self.left = left
self.right = right
def __repr__(self):
return self.left + '+' + self.right
今、私たちはできる:
>>> from symbolic import *
>>> x = Symbol('x')
>>> x+x
x+x
少しリファクタリングすることで、すべての基本的な算術を処理するように簡単に拡張できます。
class Basic(object):
def __add__(self, other):
return Add(self, other)
def __radd__(self, other): # if other hasn't implemented __add__() for Symbols
return Add(other, self)
def __mul__(self, other):
return Mul(self, other)
def __rmul__(self, other):
return Mul(other, self)
# ...
class Symbol(Basic):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class Operator(Basic):
def __init__(self, symbol, left, right):
self.symbol = symbol
self.left = left
self.right = right
def __repr__(self):
return '{0}{1}{2}'.format(self.left, self.symbol, self.right)
class Add(Operator):
def __init__(self, left, right):
self.left = left
self.right = right
Operator.__init__(self, '+', left, right)
class Mul(Operator):
def __init__(self, left, right):
self.left = left
self.right = right
Operator.__init__(self, '*', left, right)
# ...
もう少し調整するだけで、最初からsympyセッションと同じ動作を得ることができます。引数が等しい場合Add
にインスタンスを返すように変更します。インスタンスを作成する前にMul
取得しているので、これは少し注意が必要です。の代わりに使用する必要があります:__new__()
__init__()
class Add(Operator):
def __new__(cls, left, right):
if left == right:
return Mul(2, left)
return Operator.__new__(cls)
...
シンボルの等式演算子を実装することを忘れないでください。
class Symbol(Basic):
...
def __eq__(self, other):
if type(self) == type(other):
return repr(self) == repr(other)
else:
return False
...
そして出来上がり。とにかく、演算子の優先順位付け、置換による評価、高度な単純化、微分など、他のさまざまな実装方法を考えることができますが、基本が非常に単純であるのはかなりクールだと思います。