17

I'd like to be able to do:

>>> class a(str):
...     pass
...
>>> b = a()
>>> b.__class__ = str
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ assignment: only for heap types
martineau
  • 112,593
  • 23
  • 157
  • 280
Juanjo Conti
  • 27,061
  • 38
  • 104
  • 130
  • closely related: http://stackoverflow.com/questions/990758/reclassing-an-instance-in-python – balpha Jun 10 '10 at 09:57
  • 1
    I'd like to say that this is a fairly bad message from the interpreter, since the term "heap type" isn't familiar to most Python programmers and there doesn't appear to be any way in Python 3 to create a class whose instances have an assignable __class__. Or at least I haven't found one. – holdenweb Mar 30 '14 at 20:34
  • 1
    @holdenweb https://bugs.python.org/issue4600 – wim Jun 06 '19 at 17:26
  • Nice one. I meant, of course, an assignable `__class__` attribute. – holdenweb Jun 06 '19 at 18:19

4 Answers4

7

I've solved it in this way:

>>> class C(str):
...     def __getattribute__(self, name):
...         if name == '__class__':
...             return str
...         else:
...             return super(C, self).__getattribute__(name)
...         
>>> c = C()
>>> c.__class__
<type 'str'>
kay
  • 24,516
  • 10
  • 94
  • 138
Juanjo Conti
  • 27,061
  • 38
  • 104
  • 130
6

Python 2 doesn't have a unified object hierarchy (ie. not everything is descended from the object class). Anything that is part of this hierarchy can be played with via __class__, but those that aren't cannot be modified in this way (or at all, really). These are called Python's "types", and they're hard-coded in C. Examples of types are str, int, float, list, tuple, etc. This means that you cannot use types in the same ways as classes, for example you cannot change the class of an instance of a type, you cannot add, remove or modify methods of types, etc. The following transcript shows the difference in behaviour between types such as str (hard-coded, non-dynamic C constructs) and classes I've called A and B (changeable, dynamic, Python constructs):

>>> str
<type 'str'>
>>> class A:
...     pass
... 
>>> a = A()
>>> A
<class __main__.A at 0xb747f2cc>
>>> a
<__main__.A instance at 0xb747e74c>
>>> type(a)
<type 'instance'>
>>> type(A)
<type 'classobj'>
>>> type(str)
<type 'type'>
>>> type(type(a))
<type 'type'>
>>> type(type(A))
<type 'type'>
>>> A.foo = lambda self,x: x
>>> a.foo(10)
10
>>> A().foo(5)
5
>>> str.foo = lambda self,x: x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'str'
>>> 'abc'.foo(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'foo'
>>> class B:
...     pass
... 
>>> a.__class__
<class __main__.A at 0xb747f2cc>
>>> a.__class__ = B
>>> a
<__main__.B instance at 0xb747e74c>
>>> 'abc'.__class__
<type 'str'>
>>> 'abc'.__class__ = B
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ must be set to new-style class, not 'classobj' object
>>> class B(object):
...     pass
... 
>>> 'abc'.__class__ = B
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __class__ assignment: only for heap types
Anthony Sottile
  • 49,611
  • 12
  • 110
  • 158
-1

Only classes that were defined with a class keyword could be used for __class__ attribute assignment:

>>> class C:
    pass

>>> class D:
    pass

>>> C().__class__ = D
>>>
SilentGhost
  • 287,765
  • 61
  • 300
  • 288
-2

I tried this way!

>>> class C(str):
...     __class__ = str
...
>>> c = C()
>>> c.__class__
<class 'str'>