Assigning to __class__ attribute

Steven D'Aprano steve+comp.lang.python at pearwood.info
Fri Dec 3 18:26:12 EST 2010


On Fri, 03 Dec 2010 19:28:00 +0000, kj wrote:

> This was the first surprise for me: assigning to the __class__ attribute
> not only isn't vetoed, but in fact changes the instances class:
> 
> Oh-kaaaay...
> 
> First question: how kosher is this sort of class transmutation through
> assignment to __class__?  I've never seen it done.  Is this because it
> considered something to do only as a last resort, or is it simply
> because the technique is not needed often, but it is otherwise perfectly
> ok?

While not exactly encouraged, it is a deliberate feature. It's not a good 
idea to go around randomly monkey-patching instances with a new class, 
and in fact Python makes some attempts to protect you from the most 
egregious errors, but the technique has its uses. Especially if you're 
swapping between two classes which you have designed to be swapped 
between.

For instance, if you have an object that needs to display two distinct 
behaviours during different times of its life, then a good technique is 
to write two classes, one for each set of behaviours, and automatically 
switch between them as needed. A ring buffer is a common example:

http://code.activestate.com/recipes/68429-ring-buffer/

This made it into the Python Cookbook, so it comes with the approval of 
Alex Martelli, which is nearly as good as the approval of Guido :)



> The second, and much bigger, surprise comes when I attempt to do the
> same class-switching with y:
> 
>>>> y.__class__ = Ham
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> TypeError: __class__ assignment: only for heap types
> 
> (If you recall, y's class is object, the superclass of x.) Apparently
> Spam is a "heap type" (whatever that is) but its superclass, object,
> isn't.  This definitely rattles my notions of inheritance: since the
> definition of Spam was empty, I didn't expect it to have any significant
> properties that are not already present in its superclass. What's going
> on here? Is this a bug, or a feature? I can see no logical justification
> for allowing such class switching for only some class and not others.

That is more of a design compromise than a feature... it is unsafe to 
change the class of types implemented in C, for various implementation-
related reasons, and so Python prohibits it.

Heap types are dynamically allocated types that you create using the 
class statement (or the type function). They're so-called because they 
live in the heap. The builtin types defined in C presumably aren't 
dynamically allocated, but statically. Presumably they have a fixed 
layout in memory and don't live in the heap. Not being a C programmer, 
I've never cared about the implementation, only that you can't sensibly 
turn a str instance into an int instance by assigning to __class__ and so 
Python prohibits it.


> One last question:  as the questions above make clear, I have a colossal
> muddle inside my head regarding Python's model of classes and
> inheritance.  This is not for lack of trying to understand it, but,
> rather, for exactly the opposite reason: in my zeal to gain the fullest
> understanding of this topic, I think I have read too much that is
> incorrect, or obsolete, or incomplete...

I suspect you're trying to make this more complicated than it actually 
is. You keep finding little corner cases that expose implementation 
details (such as the heap-types issue above) and leaping to the erroneous 
conclusion that because you didn't understand this tiny little corner of 
Python's class model, you didn't understand any of it. Python's object 
model is relatively simple, but it does occasionally expose a few messy 
corners.


> What is the most complete, definitive, excruciatingly detailed
> exposition of Python's class and inheritance model?

That would be the source code.



-- 
Steven



More information about the Python-list mailing list