On Tue, 16 Jan 2007 08:54:09 +1100, Ben Finney wrote: > [EMAIL PROTECTED] writes: > >> Suppose you're writing a class "Rational" for rational numbers. The >> __init__ function of such a class has two quite different roles to >> play. > > That should be your first clue to question whether you're actually > needing separate functions, rather than trying to force one function > to do many different things.
[snip] > All of this points to having a separate constructor function for each > of the inputs you want to handle. [snip] > The alternate constructors are decorated as '@classmethod' since they > won't be called as instance methods, but rather: > > foo = Rational.from_string("355/113") > bar = Rational.from_int(17) > baz = Rational.from_rational(foo) That's one way of looking at it. Another way is to consider that __init__ has one function: it turns something else into a Rational. Why should the public interface of "make a Rational" depend on what you are making it from? Think of built-ins like str() and int(). I suggest that people would be *really* unhappy if we needed to do this: str.from_int(45) str.from_float(45.0) str.from_list([45, 45.5]) etc. Why do you consider that Rationals are different from built-ins in this regard? > def __add__(self, other): > result = perform_addition(self, other) > return result But that could just as easily be written as: def __add__(self, other): return perform_addition(self, other) which then raises the question, why delegate the addition out of __add__ to perform_addition? There is at least three distinct costs: a larger namespace, an extra function to write tests for; and an extra method call for every addition. What benefit do you gain? Why not put the perform_addition code directly in __add__? Just creating an extra layer to contain the complexity of rational addition doesn't gain you anything -- you haven't done anything to reduce the complexity of the problem, but you have an extra layer to deal with. And you still haven't dealt with another problem: coercions from other types. If you want to be able to add Rationals to (say) floats, ints and Rationals without having to explicitly convert them then you need some method of dispatching to different initialiser methods. (You should be asking whether you really do need this, but let's assume you do.) Presumably you create a method Rational.dispatch_to_initialisers that takes any object and tries each initialiser in turn until one succeeds, then returns the resultant Rational. Or you could just call it Rational.__init__. This doesn't mean that __init__ must or even should contain all the initialisation logic -- it could dispatch to from_string, from_float and other methods. But the caller doesn't need to call the individual initialisers -- although of course they are public methods and can be called if you want -- since __init__ will do the right thing. -- Steven D'Aprano -- http://mail.python.org/mailman/listinfo/python-list