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.