2001 Enchancement Wishlist

Tim Peters tim.one at home.com
Sun Dec 31 13:00:31 CET 2000

[Pearu Peterson]
> ...
> For example, the following behavior cannot be handled in Python 2.0:
> class NonZeroInteger:
>     def __init__(self,num):
>         if num == 0:
>             return 0
>         self.num = num
>     <other number class methods>
> so that
> >>> type(NonZeroInteger(2)),type(NonZeroInteger(0))
> (<type 'instance'>, <type 'int'>)

I bet it can't be handled that way in Python 42.0 either <0.41 wink>:  on
the very face of it, the only *sensible* thing for a NonZeroInteger
constructor to do when handed a 0 is to raise ValueError -- else its name
lies about the nature of the thing it's constructing.

Guido has Pronounced on this in the past:  a class constructor creates an
instance of the class, end of story.  It's a little bit of semantics people
can and do rely on.  For example, you didn't show any other methods of your
NonZeroInteger class, but let's say it had a method xxx.  Then any Python
programmer can rely on that

    y = NonZeroInteger(anything_at_all)

won't blow up with an AttributeError on "xxx", and without doing dynamic
type checks etc first.

There's nothing wrong with writing a factory function that can return
objects of different classes.  It often makes code using them harder to work
with, though, and precisely because you *don't* know what pops out of them
in the absence of further type checks (however spelled).

> Sure, one can write wrapper functions (as some of you already
> suggested):
> class _Complex:
>     ...
> def Complex(real,imag):
>     if imag == 0: return Real(real)
>     return _Complex(real,imag)
> but I think that this way is actually a very unpythonic one,

It's the way Guido intended from the start.  Works fine.  But you already
know that(!).

> especially if there is much simpler/cleaner solution available
> (that is, possible), that is, allowing __init__ return arbitrary
> object.

Well, I sure don't like it.

> I have used these number classes only for demonstration, but there
> are real situations where 'rich' __init__ would be very useful.

Then a factory function will be exactly as useful -- with the killer
advantage that you can actually code one today <wink>.

> For example, in PySymbolic there are classes Plus, Symbol, etc and
> gmpy numbers that are objects. E.g. class Plus represents a sum of
> its elements (arguments):
> c = Plus(a,b,c)       -> c = a + b + c  # c is Plus instance
> where a,b,c are Symbol instances.
> Now, if one constructs a Plus instance only with one argument,
> c = Plus(a)           -> c = a          # c is Symbol instance
> ***lots of code*** could be avoided with rich Plus.__init__ that would
> return in this case Symbol instance a.

Wouldn't your "rich Plus.__init__" contain about as much code as a factory
function would today?  Both have to consider all the same cases.  A factory
function would get to omit the often-useless "self" argument, though <0.9

I suggest you're off track in your modeling, confounding roles that should
be distinct:  that of Plus as a function and Plus as an object (representing
an only partially evaluated sum).  In this case I'd probably have a base
class Expression for all kinds of expresssions (incl. atoms like Symbols),
make Plus a function mapping zero or more Expressions (or subclasses) to an
Expression (or subclass), and make Sum a subclass of Expression that
represents a partially evaluated sum.  That's the way I *think* of the
domain, so that's the way I'd want to code it (and, no, I'm not bothered by
having to come up with distinct names for the verb sense (Plus) and the noun
sense (Sum) -- the distinction between nouns and verbs is usually helpful!).

    form<wink>-ly y'rs  - tim

More information about the Python-list mailing list