kj <no.em...@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 -- http://mail.python.org/mailman/listinfo/python-list