+ An overloadable piece of syntax should have a unique special name
associated, so someone who wants to overload only that piece of syntax
doesn't _have_ to wrestle many-to-one syntax-to-name mappings. This
mostly applies to __cmp__ (e.g., see post last week from the person who
wants to capture == and !=, but not the others).
A case could be made that it should apply to __len__/__nonzero__ too,
the former to overload "len", and the latter to overload Boolean
contexts; it's not _obviously_ a good idea to say those always mean the
same thing for user-defined classes. E.g., picture a "logical formula"
class, where a natural meaning for "len" is the number of atoms in the
formula, a natural meaning for __getitem__ is to return the i'th left-
to-right atom, but a natural meaning in a Boolean context is "true iff
the formula's a tautology". Note too that this kind of class
combines aspects of both sequence and numeric types (e.g., what could
be more natural than to define "|" (__or__) to mean the logical
disjunction of two formulas?).
A complication that appears obviously worthwhile here is for a __cmp__
method to override all methods corresponding to individual relational
+ The rules for __add__ and __mul__ are unlike anything else and unlike
each other. Nuke 'em, in their present form. If it's considered
valuable (& it probably is!) to get
non_instance_object binary_operator instance
to work, then that should fall out of a general scheme (i.e., one that
works for any binary_operator). Delaying specific suggestions until I
see whether y'all like it "in principle".
+ Defining a __coerce__ method should always be optional. In the absence
of a __coerce__ method,
instance binary_operator any_object
has a clear meaning (== invoke the instance method corresponding to
binary_operator, with the instance and the RHS any_object as
arguments). Even for "pure" numeric types, this is sometimes what's
wanted; e.g., in programming a matrix class, it's horribly inefficient
matrix_object / 3.0
"promote" 3 to a matrix first -- the fact that the RHS argument started
life as a scalar is important information. I currently rely on
undocumented tricks to fake this effect (e.g., in the above, making
__coerce__ hide 3.0 in an instance of an artifical _PhonyMatrix class,
whose only purpose in life is to make __coerce__ shut up <grin>).
+ While Python knows what coerce needs to do for built-in types, it can't
know what's most appropriate for user-defined types. Consequently, if
__coerce__ exists, Python should be satisified if it returns any 2-tuple
whatsoever whose 1st component is a class instance.
+ A current complication that does pay its way is that when a LHS
object's __coerce__ fails (returns None), the RHS object's __coerce__
method is tried. This really is very helpful (for those numeric types
where __coerce__ is helpful <wink>), else in a hierarchy of numeric
types each level has to know about all the others (i.e., else in
"old_type binary_operator new_type" the old_type has to know what to do
with a new_type object, and that makes a new_type at best a real pain
The primary thrust of these "principles" is to make overloading stupider
from the user's point of view; I realize that in some cases it would
actually complicate the implementation. Of course the primary advantage
to making it look stupider to the user is that synonyms for "stupid"
include "predictable" and "obvious" <wink>.
+ One simplifying option I haven't thought enough about: Suppose a
__coerce__ method only captured explicit instances of the built-in
"coerce", and implicit instances due to .sort(), "in", and __cmp__ (any
others?). Then other methods like __div__ etc would be responsible for
invoking coerce themselves, if they wanted coercion. I suspect that
would be a signficant step backwards in convenience, though; for many
"numeric" types, implicit coercion really is a help.
Tim Peters firstname.lastname@example.org
not speaking for Kendall Square Research Corp