When where and how can i change the __class__ attr of an object?
Asked Answered
M

4

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
Militant answered 10/6, 2010 at 9:45 Comment(5)
closely related: #991258Malone
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.Ewer
@Ewer bugs.python.org/issue4600Triangular
Nice one. I meant, of course, an assignable __class__ attribute.Ewer
The issue here isn't when or where the attribute is assigned; it's what you want to change it to.Kepi
M
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'>
Militant answered 14/6, 2010 at 7:46 Comment(0)
F
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
Foreshank answered 26/3, 2011 at 15:30 Comment(3)
Everything in python 2 is a object instance.Dizon
You mean that not all classes are instances of type.Dizon
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).Kepi
G
-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
>>>
Gisele answered 10/6, 2010 at 9:54 Comment(4)
That´s not what they say here:bytes.com/topic/python/answers/449635-assigning-self-classMilitant
In fact, I think __mro__ is the only read-only attribute there.Osy
I think this is not total true. You may have class A(str) and you cant assign A.Militant
See mail-archive.com/[email protected]/msg52950.html for a list of differences between types that might give troubles.Ceiling
S
-2

I tried this way!

>>> class C(str):
...     __class__ = str
...
>>> c = C()
>>> c.__class__
<class 'str'>
Sinter answered 5/3, 2019 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.