Hiya, This is really more a general programming question than a python-in-edu question, but it's related to project for education that I'm working on, and based on lurking for a while I'm pretty sure some members of the list will have some interest and expertise to answer. Say I have some class definitions like this: class Expression: stuff class LinearExpression(Expression): stuff class QuadraticExpression(Expression): stuff Is there any way to make the Expression constructor return an instance of one of the subclasses (based on its arguments) rather than an Expression instance. E.G. e=Expression("3x+5") e.__class__ == <class LinearExpression> e=Expression("x^2-9") e.__class__ == <class QuadraticExpression> I can think of plenty of workarounds involving helper functions and I can think of a number reason why idiom given above might not be The Right Thing whether or not it is possible. But I can't help thinking that there is an elegance to this approach: Let the superclass choose the appropiate subclass as an implementation, based on what it is supposed to represent. Similarly, can a class constructor choose not to construct a class and instead return a base type object? For example (assuming Fraction is a suitably designed class for rationals), can you do: f=Fraction(1,2) # Fraction(n,d) => n/d type(f) == <type 'instance'> f=Fraction(2,1) type(f) == <type 'int'> Again, I can think of reasons not to do this, but it seems reasonable to let the class pick the most appropriate implementation of itself as possible, and if that is a builtin type, why not? It's probably obvious from my examples that I'm interested in computer algebra. I've found a few python CA systems: fewer than I'd expect, more than I'd like to evaluate one by one, none well documented enough for me to guess how easy to use/featureful they are. Anybody got any advice/pointers for good, extensible computer algebra implementations under python? Thanks, Jay
The idiom you're asking about is called 'Virtual Constructors'. James Coplien's 'Advanced C++ Programming Styles and Idioms' describes a roundabout way to implement such things in C++. In Python, however, since class creation looks like a function call, it would be just as easy to implement this with (essentially) helper functions, e.g., def Expression(*args): "Construct an object of the Expression class or a subclass" if <blah blah blah>: return cLinearExpression(*args) elif <blah blah blah>: return cQuadraticExpression(*args) class cExpression: blah blah blah class cLinearExpression(cExpression): blah blah blah this could easily be extended so that each potential subclass "registers" itself with the virtual constructor, so that multiple independent modules could extend a single virtual superclass constructor. In module expression.py: Expression_subclasses = [] def Expression_register_subclass(subclass, test_fn): Expression_subclasses.append((subclass, test_fn)) def Expression(*args): for subclass, test_fn in Expression_subclasses: if test_fn(*args): return subclass(*args) return None class cExpression: blah blah blah and in module quadratic.py: from expression import * class cQuadraticExpression(cExpression): blah blah blah def is_quadratic(*args): blah blah blah Expression_register_subclass(cQuadraticExpression, is_quadratic) Hope this helps. -- Dustin Mitchell dustin@cs.uchicago.edu dustin@ywlcs.org
It's probably obvious from my examples that I'm interested in computer algebra. I've found a few python CA systems: fewer than I'd expect, more than I'd like to evaluate one by one, none well documented enough for me to guess how easy to use/featureful they are. Anybody got any advice/pointers for good, extensible computer algebra implementations under python?
Thanks, Jay
Although by no means a full fledged CA system, I've done some stuff with algebra in Python. Here's an example session:
from mathobjects import *
p = Poly([3,4,5]) p 3*x**2 + 4*x + 5
q = Poly([1,2,3,4]) q x**3 + 2*x**2 + 3*x + 4 q.deriv() 3*x**2 + 4*x + 3
p*q 3*x**5 + 10*x**4 + 22*x**3 + 34*x**2 + 31*x + 20 p(10) 345L
F = Fraction f = F(1,2) f (1/2)
f*p (3/2)*x**2 + 2*x + (5/2)
p.roots() [(-0.66666666666666663+1.1055415967851332j), (-0.66666666666666663-1.1055415967851332j)]
A = Sqmatrix([[F(1,2),1],[F(3/2),-2]]) A [(1/2), 1] [1, -2] A.inverse() [1, (1/2)] [(1/2), (-1/4)]
[q.roots() caused an error -- used to work but I started stripping out conversions to longs when 2.2 made this automatic, then putting back in when I decided to keep 2.1.1 back compatibility, and there's still something broken around here. Hope to fix it soon]. The current source code is at http://www.oregon-tips.com/ under Curricula | Python. There's also a bernoulli numbers piece. Kirby
participants (3)
-
Dustin Mitchell -
Jay Bloodworth -
Kirby Urner