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__





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





@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.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)