Here's an example of a problem that I've recently come up against for

the umpteenth time. It's not difficult to solve, but my previous

solutions have never seemed quite right, so I'm writing to ask whether

others have encountered this problem, and if so what solutions they've

come up with.

Suppose you're writing a class "Rational" for rational numbers. The

__init__ function of such a class has two quite different roles to

play. First, it's supposed to allow users of the class to create

Rational instances; in this role, __init__ is quite a complex beast.

It needs to allow arguments of various types---a pair of integers, a

single integer, another Rational instance, and perhaps floats, Decimal

instances, and suitably formatted strings. It has to validate the

input and/or make sure that suitable exceptions are raised on invalid

input. And when initializing from a pair of integers---a numerator

and denominator---it makes sense to normalize: divide both the

numerator and denominator by their greatest common divisor and make

sure that the denominator is positive.

But __init__ also plays another role: it's going to be used by the

other Rational arithmetic methods, like __add__ and __mul__, to return

new Rational instances. For this use, there's essentially no need for

any of the above complications: it's easy and natural to arrange that

the input to __init__ is always a valid, normalized pair of integers.

(You could include the normalization in __init__, but that's wasteful

when gcd computations are relatively expensive and some operations,

like negation or raising to a positive integer power, aren't going to

require it.) So for this use __init__ can be as simple as:

def __init__(self, numerator, denominator):

self.numerator = numerator

self.denominator = denominator

So the question is: (how) do people reconcile these two quite

different needs in one function? I have two possible solutions, but

neither seems particularly satisfactory, and I wonder whether I'm

missing an obvious third way. The first solution is to add an

optional keyword argument "internal = False" to the __init__ routine,

and have all internal uses specify "internal = True"; then the

__init__ function can do the all the complicated stuff when internal

is False, and just the quick initialization otherwise. But this seems

rather messy.

The other solution is to ask the users of the class not to use

Rational() to instantiate, but to use some other function

(createRational(), say) instead. Then __init__ is just the simple

method above, and createRational does all the complicated stuff to

figure out what the numerator and denominator should be and eventually

calls Rational(numerator, denomiator) to create the instance. But

asking users not to call Rational() seems unnatural. Perhaps with

some metaclass magic one can ensure that "external" calls to

Rational() actually go through createRational() instead?

Of course, none of this really has anything to do with rational

numbers. There must be many examples of classes for which internal

calls to __init__, from other methods of the same class, require

minimal argument processing, while external calls require heavier and

possibly computationally expensive processing. What's the usual way

to solve this sort of problem?

Mark