How dangerous is setting self.__class__ to something else?
Assigning the __class__
attribute is useful if you have a long time running application and you need to replace an old version of some object by a newer version of the same class without loss of data, e.g. after some reload(mymodule)
and without reload of unchanged modules. Other example is if you implement persistency - something similar to pickle.load
.
All other usage is discouraged, especially if you can write the complete code before starting the application.
Here's a list of things I can think of that make this dangerous, in rough order from worst to least bad:
- It's likely to be confusing to someone reading or debugging your code.
- You won't have gotten the right
__init__
method, so you probably won't have all of the instance variables initialized properly (or even at all). - The differences between 2.x and 3.x are significant enough that it may be painful to port.
- There are some edge cases with classmethods, hand-coded descriptors, hooks to the method resolution order, etc., and they're different between classic and new-style classes (and, again, between 2.x and 3.x).
- If you use
__slots__
, all of the classes must have identical slots. (And if you have the compatible but different slots, it may appear to work at first but do horrible things…) - Special method definitions in new-style classes may not change. (In fact, this will work in practice with all current Python implementations, but it's not documented to work, so…)
- If you use
__new__
, things will not work the way you naively expected. - If the classes have different metaclasses, things will get even more confusing.
Meanwhile, in many cases where you'd think this is necessary, there are better options:
- Use a factory to create an instance of the appropriate class dynamically, instead of creating a base instance and then munging it into a derived one.
- Use
__new__
or other mechanisms to hook the construction. - Redesign things so you have a single class with some data-driven behavior, instead of abusing inheritance.
As a very most common specific case of the last one, just put all of the "variable methods" into classes whose instances are kept as a data member of the "parent", rather than into subclasses. Instead of changing self.__class__ = OtherSubclass
, just do self.member = OtherSubclass(self)
. If you really need methods to magically change, automatic forwarding (e.g., via __getattr__
) is a much more common and pythonic idiom than changing classes on the fly.