What happens internally when I press Enter?
My motivation for asking, besides plain curiosity, is to figure out what happens when you
from sympy import *
and enter an expression. How does it go from Enter to calling
__sympifyit_wrapper(a,b)
in sympy.core.decorators? (That's the first place winpdb took me when I tried inspecting an evaluation.) I would guess that there is some built-in eval function that gets called normally, and is overridden when you import sympy?
SymPy is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS) while keeping the code as simple as possible in order to be comprehensible and easily extensible.
Python interactive shell is also known as Integrated Development Environment (IDLE). With the Python installer, two interactive shells are provided: one is IDLE (Python GUI) and the other is Python (command line). Both can be used for running simple programs.
All right after playing around with it some more I think I've got it.. when I first asked the question I didn't know about operator overloading.
So, what's going on in this python session?
>>> from sympy import *
>>> x = Symbol(x)
>>> x + x
2*x
It turns out there's nothing special about how the interpreter evaluates the expression; the important thing is that python translates
x + x
into
x.__add__(x)
and Symbol inherits from the Basic class, which defines __add__(self, other)
to return Add(self, other)
. (These classes are found in sympy.core.symbol, sympy.core.basic, and sympy.core.add if you want to take a look.)
So as Jerub was saying, Symbol.__add__()
has a decorator called _sympifyit
which basically converts the second argument of a function into a sympy expression before evaluating the function, in the process returning a function called __sympifyit_wrapper
which is what I saw before.
Using objects to define operations is a pretty slick concept; by defining your own operators and string representations you can implement a trivial symbolic algebra system quite easily:
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
Now we can do:
>>> from symbolic import *
>>> x = Symbol('x')
>>> x+x
x+x
With a bit of refactoring it can easily be extended to handle all basic arithmetic:
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)
# ...
With just a bit more tweaking we can get the same behavior as the sympy session from the beginning.. we'll modify Add
so it returns a Mul
instance if its arguments are equal. This is a bit trickier since we have get to it before instance creation; we have to use __new__()
instead of __init__()
:
class Add(Operator):
def __new__(cls, left, right):
if left == right:
return Mul(2, left)
return Operator.__new__(cls)
...
Don't forget to implement the equality operator for Symbols:
class Symbol(Basic):
...
def __eq__(self, other):
if type(self) == type(other):
return repr(self) == repr(other)
else:
return False
...
And voila. Anyway, you can think of all kinds of other things to implement, like operator precedence, evaluation with substitution, advanced simplification, differentiation, etc., but I think it's pretty cool that the basics are so simple.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With