[Python-Dev] type(obj) vs. obj.__class__

David Mertz mertz at gnosis.cx
Sun Oct 18 22:12:34 EDT 2015


My intuition differs from Steven's here.  But that's fine.  In any case, my
simple implementation of RingBuffer in this thread avoids either rebinding
methods or changing .__class__.

And yes, of course collections.deque is better than any of these
implementations.  I was just trying to show that any such magic is unlikely
to be necessary... and in particular that the recipe given as an example
doesn't show it is.

But still, you REALLY want your `caterpillar = Caterpillar()` to become
something of type "Butterfly" later?! Obviously I understand the biological
metaphor.  But I'd much rather have an API that provided me with
.has_metamorphosed() then have to look for the type as something new.

Btw. Take a look at Alex' talk with Anna at PyCon 2015.  They discuss
various "best practices" that have been superseded by improved language
facilities.  They don't say anything about this "mutate the __class__
trick", but I somehow suspect he'd put that in that category.

On Sun, Oct 18, 2015 at 6:47 PM, Steven D'Aprano <steve at pearwood.info>
wrote:

> On Sun, Oct 18, 2015 at 05:35:14PM -0700, David Mertz wrote:
>
> > In any case, redefining a method in a certain situation feels a lot less
> > magic to me than redefining .__class__
>
> That surprises me greatly. As published in the Python Cookbook[1], there
> is a one-to-one correspondence between the methods used by an object and
> its class. If you want to know what instance.spam() method does, you
> look at the class type(instance) or instance.__class__, and read the
> source code for spam.
>
> With your suggestion of re-defining the methods on the fly, you no
> longer have that simple relationship. If you want to know what
> instance.spam() method does, first you have to work out what it actually
> is, which may not be that easy. In the worst case, it might not be
> possible at all:
>
> class K:
>     def method(self):
>         if condition:
>             self.method = random.choice([lambda self: ...,
>                                          lambda self: ...,
>                                          lambda self: ...])
>
>
> Okay, that's an extreme example, and one can write bad code using any
> technique. But even with a relatively straight-forward version:
>
>     def method(self):
>         if condition:
>             self.method = self.other_method
>
>
> I would classify "change the methods on the fly" as self-modifying code,
> which strikes me as much more hacky and hard to maintain than something
> as simple as changing the __class__ on the fly.
>
> Changing the __class__ is just a straight-forward metamorphosis: what
> was a caterpillar, calling methods defined in the Caterpillar class, is
> now a butterfly, calling methods defined in the Butterfly class.
>
> (The only change I would make from the published recipe would be to make
> the full Ringbuffer a subclass of the regular one, so isinstance() tests
> would work as expected. But given that the recipe pre-dates the
> wide-spread use of isinstance, the author can be forgiven for not
> thinking of that.)
>
> If changing the class on the fly is a metamorphosis, then it seems to me
> that self-modifying methods are like something from The Fly, where a
> horrible teleporter accident grafts body parts and DNA from one object
> into another object... or at least *repurposes* existing methods, so
> that what was your leg is now your arm.
>
> I've done that, and found it harder to reason about than the
> alternative:
>
> "okay, the object is an RingBuffer, but is the append method the
> RingBuffer.append method or the RingBuffer.full_append method?"
>
> versus
>
> "okay, the object is a RingBuffer, therefore the append method is the
> RingBuffer.append method".
>
>
> In my opinion, the only tricky thing about the metamorphosis tactic is
> that:
>
> obj = Caterpillar()
> # later
> assert type(obj) is Caterpillar
>
> may fail. You need a runtime introspection to see what the type of obj
> actually is. But that's not exactly unusual: if you consider Caterpillar
> to be a function rather than a class constructor (a factory perhaps?),
> then it's not that surprising that you can't know what *specific*
> type a function returns until runtime. There are many functions with
> polymorphic return types.
>
>
>
>
>
> [1] The first edition of the Cookbook was edited by Python luminaries
> Alex Martelli and David Ascher, so this recipe has their stamp of
> approval. This isn't some dirty hack.
>
>
> --
> Steve
> _______________________________________________
> Python-Dev mailing list
> Python-Dev at python.org
> https://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe:
> https://mail.python.org/mailman/options/python-dev/mertz%40gnosis.cx
>



-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20151018/62cfd057/attachment.html>


More information about the Python-Dev mailing list