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
  • 119,623
  • 25
  • 170
  • 301
Juanjo Conti
  • 28,823
  • 42
  • 111
  • 133
  • 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
  • The issue here isn't when or where the attribute is assigned; it's *what* you want to change it *to*. – Karl Knechtel Mar 03 '23 at 17:00

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'>
Kijewski
  • 25,517
  • 12
  • 101
  • 143
Juanjo Conti
  • 28,823
  • 42
  • 111
  • 133
6

Python 2 doesn't have a unified object hierarchy (ie. not everything is derived from the object). 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). Python 2 calls these "types" rather than "classes", 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
Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • Everything in python 2 is a object instance. – Pavel Patrin Feb 15 '17 at 09:10
  • You mean that not all classes are instances of type. – Pavel Patrin Feb 15 '17 at 09:12
  • In 3.x, the built-in types are referred to the same way as user-defined types (both say "class" rather than "type" in object representations), and they are considered subclasses of `object`, but the issue identified by OP persists (because the types are still implemented in a different way with a fundamentally different memory layout). – Karl Knechtel Mar 03 '23 at 17:03
-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
  • 307,395
  • 66
  • 306
  • 293
-2

I tried this way!

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