One Python 2.1 idea

Alex Martelli aleaxit at yahoo.com
Fri Dec 29 00:50:35 CET 2000


"Tim Peters" <tim.one at home.com> wrote in message
news:mailman.978038049.481.python-list at python.org...
    [snip]
> > What are the 'good Pythonic ways' to do this, as opposed to
> > 'dirty tricks'?
> > ... [assorted trickery with the "new" module] ...
>
> class UniqueOgreFoo(Ogre):
>     foo = fun
> x.__class__ = UniqueOgreFoo
>
> x.foo() # now prints 'have fun!'

Right.  So, in the general case (where the instance to 'uniquize',
the methodname to uniquize it for, and the function-object to
use for the purpose, are all runtime-determined, rather than
constants as in this toy-example), it would be:

def uniquize(instance, methodname, functionobject):
    class Uniqued(instance.__bases__):
        pass
    setattr(Uniqued, methodname, functionobject)
    instance.__class__ = Uniqued

rather than

def uniquize(instance, methodname, functionobject):
    method = new.instancemethod(functionobject,
        instance, instance.__class__)
    setattr(instance, methodname, method)

right?  OK, I'll take your word for it -- they seem about equally
'dark' to me, and the former surely has the no-refloop advantage,
after all.  But, apart from the historical accident that 'new' is,
well, new, wouldn't the latter be normally more Pythonic, i.e.,
less obscure?  The former relies on the magic and implicit
function->method conversion that classes do, the latter makes
the conversion-to-method quite explicit.


> obscurity of the code is a bigger issue in practice.  Not that mucking
> around with __class__ is recommended to begin with!  That's obscure too,
> just not as obscure as new.*.  Best would have been to make x an instance
of
> UniqueOgreFoo from the start.

Sure!  The assumption is that one _can't_ use that simplest
of approaches, because the need to uniquize x that way is
determined dynamically; i.e., that the behavior of x.foo()
must, some way or other, be now made different from what
it used to be before.

If this sort of thing happened a few times to instances of
class Ogre in a typical program run, would it be worth
refactoring Ogre to take this into account -- perhaps through
a suitable metaclass, to check an instance 'uniquization'
dictionary for possible 'uniquized' methods before going
the normal class-dictionary way, or would _that_ be too
obscure too?  My personal perception is that metaclasses
are 'obscurer' than either new, or setting __class__, but
that counts for little, since I don't see that the latter is
any clearer than the former, so I must not be seeing through
properly Pythonic eyes.  Or would a refactoring by more
elementary means, with more explicit delegation, be more
Pythonic?  E.g.:

class Ogre:
    class TypicalOgre:
        [snipped: methods as before]
    prototype = TypicalOgre()
    def __init__(self):
        self.__unique = {}
    def __getattr__(self, name):
        try:
            return self.__unique[name]
        except KeyError:
            Ogre.__temp = getattr(TypicalOgre, name)
            return self.__temp
    def uniquize(self, methodname, functionobject):
        Ogre.__temp = functionobject
        self.__unique[methodname] = self.__temp

Hmmm -- this looks cumbersome to me, and unPythonic
in relying deeply on the class's implicit function->method
magic transformation; not to mention the overhead AND
the reference-loop, plus the fact that, this way, it only
works for one class (or, at best, it can be packaged up as
a mixin), while previous approaches were more general.

So I guess that, if one does have the need for such
dynamic calisthenics, changing __class__ is still best?


> give-a-man-a-fish-and-you've-fed-him-for-a-day-but-teach-a-man-to-
>     fish-and-he'll-come-by-every-day-asking-for-bait-lures-spare-
>     lines-and-your-best-pole<wink>-ly y'rs  - tim

Right!  No good deed goes unpunished, remember... (why, I'll even
tolerate the <winks> in exchange for your precious lessons!-).


Alex






More information about the Python-list mailing list