Tweaks for using python as a calculator are used in my package calcpy.
What it basically does is using IPython's hooks to change any occurence of a number to sympy's Rational:
import ast
from sympy import Rational, init_printing
class ReplaceFloatWithRational(ast.NodeTransformer):
def visit_Constant(self, node):
if isinstance(node.value, float):
return ast.Call(func=ast.Name(id='Rational', ctx=ast.Load()),
args=[ast.Call(func=ast.Name(id='str', ctx=ast.Load()),
args=[node], keywords=[])],
keywords=[])
return self.generic_visit(node)
class ReplaceIntegerDivisionWithRational(ast.NodeTransformer):
def visit_BinOp(self, node):
def is_integer(x):
if isinstance(x, ast.Constant) and isinstance(x.value, int):
return True
if isinstance(x, ast.UnaryOp) and isinstance(x.op, (ast.USub, ast.UAdd)):
return is_integer(x.operand)
if isinstance(x, ast.BinOp) and isinstance(x.op, (ast.Add, ast.Sub, ast.Mult, ast.Pow)):
return is_integer(x.left) and is_integer(x.right)
return False
if (isinstance(node.op, ast.Div) and is_integer(node.left) and is_integer(node.right)):
return ast.Call(func=ast.Name(id='Rational', ctx=ast.Load()),
args=[node.left, node.right], keywords=[])
return self.generic_visit(node)
get_ipython().ast_transformers.append(ReplaceFloatWithRational())
get_ipython().ast_transformers.append(ReplaceIntegerDivisionWithRational())
init_printing()
Example:
In [1]: 1/2 + 2/3
Out[1]: 7/6
This would convert all numbers to fractions, on calcpy numbers are printed both as fractions and as decimals.