Returning different types based on input parameters

MRAB google at mrabarnett.plus.com
Mon Apr 6 17:56:11 EDT 2009


George Sakkis wrote:
> That's more of a general API design question but I'd like to get an
> idea if and how things are different in Python context. AFAIK it's
> generally considered bad form (or worse) for functions/methods to
> return values of different "type" depending on the number, type and/or
> values of the passed parameters. I'm using "type" loosely in a duck-
> typing sense, not necessarily as a concrete class and its descendants,
> although I'm not sure if even duck-typing is endorsed for return
> values (as opposed to input parameters).
> 
> For example, it is common for a function f(x) to expect x to be simply
> iterable, without caring of its exact type. Is it ok though for f to
> return a list for some types/values of x, a tuple for others and a
> generator for everything else (assuming it's documented), or it should
> always return the most general (iterator in this example) ?
> 
> To take it further, what if f wants to return different types,
> differing even in a duck-type sense? That's easier to illustrate in a
> API-extension scenario. Say that there is an existing function `solve
> (x)` that returns `Result` instances.  Later someone wants to extend f
> by allowing an extra optional parameter `foo`, making the signature
> `solve(x, foo=None)`. As long as the return value remains backward
> compatible, everything's fine. However, what if in the extended case,
> solve() has to return some *additional* information apart from
> `Result`, say the confidence that the result is correct ? In short,
> the extended API would be:
> 
>     def solve(x, foo=None):
>         '''
>         @rtype: `Result` if foo is None; (`Result`, confidence)
> otherwise.
>         '''
> 
> Strictly speaking, the extension is backwards compatible; previous
> code that used `solve(x)` will still get back `Result`s. The problem
> is that in new code you can't tell what `solve(x,y)` returns unless
> you know something about `y`. My question is, is this totally
> unacceptable and should better be replaced by a new function `solve2
> (x, foo=None)` that always returns (`Result`, confidence) tuples, or
> it might be a justifiable cost ? Any other API extension approaches
> that are applicable to such situations ?
> 
I don't like the sound of this. :-)

In your example I would possibly suggest returning a 'Result' object and
then later subclassing to give 'ConfidenceResult' which has the
additional 'confidence' attribute.

I think the only time when it's OK to return instances of different
classes is when one of them is None, for example the re module where
match() returns either a MatchObject (if successful) or None (if
unsuccessful); apart from that, a function should always return an
instance of the same class (or perhaps a subclass) or, if a collection
then the same type of collection (eg always a list and never sometimes a
list, sometimes a tuple).



More information about the Python-list mailing list