total idiot question: +=, .=, etc...

Tim Peters tim_one at email.msn.com
Mon Jun 28 00:34:50 EDT 1999


[Neel Krishnaswami]
> ...
> Here's an example from some code of mine (it's from a programmatic
> front-end to Dejanews):
>
> class Article:
>     [...]
>     #
>     # To set the sort order, set Article.sortorder to one of the
>     # keys of Article.comparators.
>     #
>     comparators = {'date':      lambda x,y: cmp(x.date,y.date),
>                    'score':     lambda x,y: cmp(x.score,y.score),
>                    'author':    lambda x,y: cmp(x.author,y.author),
>                    'subject':   lambda x,y: cmp(x.subject,y.subject),
>                    'newsgroup': lambda x,y: cmp(x.newsgroup,y.newsgroup)
>                    }
>     sortorder = 'date' # can be anything in Article.comparators.keys()
>
>     [...]
>
>     def __cmp__(self, other):
>         return self.__class__.comparators[self.__class__.sortorder](self,
>                                                                     other)
>
> I want the user to be able to determine how a list of Article's can be
> sorted by setting the class's sortorder member. So a statement like
>
> >>> Article.sortorder = 'author'
>
> would change how lists of articles are sorted. This is definitely
> something that's shouldn't be an instance attribute, imo; it would
> defeat the purpose if each article could compare differently than
> any other. (Oppose standardized testing! :])
>
> But look how ugly that __cmp__ method is; it would be a lot clearer to
> write something like
>
>     def __cmp__(self, other):
>         return Article.comparators[Article.sortorder](self, other)
>
> because that would make it clearer what class state is being used, and
> why. However, though it would be clearer to the programmer, it would
> profoundly mess up inheritance.
>
> It's perfectly reasonable for someone else to subclass Article and
> override the Article.comparators attribute. (Say to add an ordering to
> put Tim Peter's posts at the top of the list,

Yes, an excellent idea!

> or to use something faster than my lambdas.)

Oh, yawn <wink>.

> With the clear way of writing it, the NewArticle class would
> mysteriously fail to do the right thing, because the __cmp__ method
> would look in the Article class dictionary without bothering to check
> NewArticle's.

The trap is hard to avoid, though:  if a direct instance of Article is
"self", and a direct instance of NewArticle is "other", even keying off
self.__class__.etc is a surprise (well, at least to "other"!).  In part
you're taking a single-dispatch approach to a problem with multiple-dispatch
headaches.  Face that head-on and you'll write a multiple-argument
dispatcher with which the various classes need to register their intentions
("OK, if arg1 and arg2 are both Articles, I need to compare such-&-such a
way; if arg1 is Article but arg2 is NewArticle, that-&-that a way;" etc).
Then it's factored out of the xxxArticle classes.

> This is an unusual glitch in Python; usually the readable way of doing
> something is the right way of doing it.

Guido long ago confessed he had static class hierarchies in mind, where
everyone knew who they were inheriting from for all time so that "super" etc
weren't needed.  That should probably be re-thought for Python2, since it
has come up more than once <wink>.  Writing

    class A(B):
        def __init__(self, x, y, z):
            B.__init__(self, x, y, z)

can be grating too (hmm ... I see Michael and Greg are whining about that
too, so I'll skip it).

In your particular case, the solution that plays along with the current
design is to forget direct access *and* __class__, instead indirecting
through a method:

class Article:
    comparators = { ... }
    sortorder = { ... }
    def get_class_comparators(self):
        return Article.comparators
    def get_class_sortorder(self):
        return Article.sortorder
    def __cmp__(self, other):
        return self.get_class_comparators() ...

and override get_class_comparators/sortorder() in NewArticle.  Perfectly
clear and ridiculously involved <wink>.  The fact is that self.comparators
does the search that you want, so that's the best way to do it -- live with
a comment.

> Maybe we could re-use the class keyword and let people write class.foo
> inside class definitions, by analogy to writing self.foo to refer to
> the current instance? And the class.foo syntax would get translated to
> the equivalent of self.__class__?

Except that you want class.foo to act like self.__class__.foo <wink>.  Guido
certainly won't change this in Python1 regardless; take it up on the
Types-SIG for Python2!

more-dead-ideas-there-than-in-kosovo-ly y'rs  - tim






More information about the Python-list mailing list