Overwriting __class__ raises TypeError
Overwriting __class__ raises TypeError
I'm trying to extend functionality of a module class written in C by deriving from that class and overwriting / adding certain methods*.
Problem is that his module creates instances of the class I'm trying to extend at various different places. I thus need to create a casting method to cast the base class provided by the module to my own derived class with the additonal functionality.
Here's what I've tried:
class Derived(Base):
@classmethod
def cast(cls, obj: Base):
obj.__class__ = cls
This method works with class hierarchies that I've created myself - however it fails with the module I use, throwing the following exception:
TypeError: __class__ assignment only supported for heap types or ModuleType subclasses
I'm having a hard time finding official information about this exception. Any info helps, I'd even accept hackish solutions so long as they're clean and fully mimic the behavior I'km looking for.
* The class I'm trying to extend is Surface
inside the package pygame
.
Surface
pygame
1 Answer
1
The __class__
attribute has always been restricted in what is acceptable, and Python classes can be a lot more flexible than types defined in C are.
__class__
For example, the __slots__
section in the datamodel documentation states:
__slots__
__class__
assignment works only if both classes have the same __slots__
.
__class__
__slots__
and the same document calls the instance.__class__
attribute read only:
instance.__class__
The implementation adds a few special read-only attributes to several object types, where they are relevant.
So __class__
was actually not meant to be writeable, but it has been for a long time and a is very useful property.
__class__
Now, in your case the assignment isn't permitted because the target instances are of a type that doesn't allocate the objects on a heap (an area of process memory that grows to accommodate an arbitrary number of objects, it's where most Python objects are allocated). Objects not allocated on the heap are managed differently (not subject to reference counting for example), and if you changed their type they'd suddenly need to be managed differently. That's currently not supported.
It was briefly supported in Python 3.5 release candidate builds, to allow setting __class__
on modules, but that backfired once it was discovered that it was possible to alter the type of interned immutable values:
__class__
class MyInt(int):
# lets make ints mutable!
(1).__class__ = MyInt
1
is an interned value, so now all use of the integer 1
everywhere in the Python program has been changed. Not what you want, especially if you are re-using interpreter memory across multiple processes the way the Google App Engine does! See issue #24912 for the low-down.
1
1
But this is why module instances are specifically mentioned in the exception, see Customising module attribute access.
You'll have to find a different path to solving your problem. For example, perhaps your specific problem can be solved instead by wrapping the instances in something that uses __getattr__
to proxy attributes to the wrapped instance.
__getattr__
@Nearoo: you can't work around it. You'll have to find a different approach, perhaps by wrapping the instances or coding up a type in C to bypass these restrictions.
– Martijn Pieters♦
Sep 2 at 21:07
I see. A wrapper class it is, then. Thank you!
– Nearoo
Sep 2 at 21:17
Would you mind cleaning up your answer a bit? Shorting explaination and instead linking to official sources, and also suggest a workaround. Wrapper classes seem to work nicely. I found a neat one here
– Nearoo
Sep 2 at 21:19
@Nearoo: a wrapper class is not a general solution, it won't work for all situations where you have instances of one type that you want to force to be a different type instead.
– Martijn Pieters♦
Sep 2 at 21:21
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I see. Is there a workaround? Can I transfer the object onto the heap? Re-initialize it, maybe? I mean I can derive from the non-heap object just fine and change it like that.
– Nearoo
Sep 2 at 21:06