Assigning to __class__ attribute

Arnaud Delobelle arnodel at gmail.com
Fri Dec 3 16:11:11 EST 2010


kj <no.email at please.post> writes:

> I have a couple of questions regarding assigning to an instance's
> __class__ attribute.
>
> The first is illustrated by the following interaction.  First I
> define an empty class:
>
>>>> class Spam(object): pass
> ... 
>
> Now I define an instance of Spam and an instance of Spam's superclass:
>>>> x = Spam()
>>>> y = Spam.__mro__[1]() # (btw, is there a less uncouth way to do this???)

Yes:

    y = object()

>>>> [z.__class__.__name__ for z in x, y]
> ['Spam', 'object']
>
> Now I define a second empty class:
>>>> class Ham(object): pass
> ... 
>
> Next, I attempt to assign the value Ham to x.__class__:
>
>>>> x.__class__ = Ham
>>>> [isinstance(x, z) for z in Spam, Ham]
> [False, True]
>
> 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?

It's OK as long as the slots defined in the classes are the same (using
Python 3 below so no need for specifying that classes derive from object):

>>> class Foo: pass
... 
>>> class Bar: __slots__ = 'x', 'y'
... 
>>> foo = Foo()
>>> foo.__class__ = Bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ assignment: 'Foo' object layout differs from 'Bar'
>>> class Baz: __slots__ = 'x', 'y'
... 
>>> bar = Bar()
>>> bar.__class__ = Baz


> 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

y is of type object, which is a builtin type.  You can only switch the
__class__ of an instance of a user-defined class.

> (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.

There is a big difference:

>>> class Foo: pass
>>> x, y = Foo(), object()
>>> x.__dict__
{}
>>> y.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute '__dict__'

This means that you can have instance attributes for x but not for y:

>>> x.myattr = 123
>>> y.myattr = 123
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'myattr'

This reflects the fact that x and y are a different kind of
object, with a different layout so you can't hotswap their types. 

I imagine that the term "heap type" is used to types which are objects
that live on the heap (in practice they are types defined in Python,
by using a "class" statement or calling type(name, bases, attrs)) as
opposed to builtin types such as object, int, str, etc... which don't
live on the heap.

> 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...
> What is the most complete, definitive, excruciatingly detailed
> exposition of Python's class and inheritance model?

I learnt by reading Guido's "Unifying types and classes in Python 2.2",
available here:

    http://www.python.org/download/releases/2.2.3/descrintro/

-- 
Arnaud



More information about the Python-list mailing list