Dynamically replacing an objects __class__; is it safe?

Steve D'Aprano steve+python at pearwood.info
Wed Mar 15 09:53:04 EDT 2017


On Wed, 15 Mar 2017 08:54 pm, marco.nawijn at colosso.nl wrote:

> Dear All,
> 
> Summary of the question:
> Is it generally safe to dynamically change an objects class; if not
> under which conditions can it be considered safe.

*Generally* safe? No. You cannot expect to take an arbitrary object and
change its class:


animal = Whale()
animal.__class__ = Bird
animal.fly()


You probably can't make a whale fly just by changing the class to bird. It
will need wings, and feathers, at the very least.

For pure-Python classes, it is "safe" in the sense that the worst that will
happen is an exception, not a seg fault or core dump. But it is not safe in
the sense that the instance will work correctly: methods may fail, or do he
wrong thing.

(Actually, *silently doing the wrong thing* is usually much worse than
raising an exception or crashing. At least when the program crashes, you
know it is broken!)

For *cooperative* classes, where you design the Before and After classes to
operate correctly when you change the __class__, that's perfectly safe.


> Context:
> Given the code below, I have no direct control over Base and M1. M1
> is a instantiated by 'calling' the read-only property of Base.
> I would like to transparently switch behaviour from M1 to M2. I have
> tried several things, but finally settled on the code below. Although
> it works really well, I am not sure whether I am about to shoot myself
> in the foot. In short, what I do is derive from Base, shadow the read-only
> property 'my_prop' so it returns M2 and finally replace an objects
> __class__ attribute with my derived class.

Sounds scarily confusing, and I say that as somebody who LOVES the "swap the
__class__" trick when it is appropriate. If I wouldn't risk swapping out
the __class__ if I didn't control the classes. You have this:


o = Base()
o.my_prop
# Prints 'Initializing M1'

o = Base()
o.__class__ = ShadowBase
o.my_prop
# Prints 'Initializing M2'


But why not just do this?

o = ShadowBase()  # inherits from Base, overrides my_prop
o.my_prop
# Prints 'Initializing M2'


This sounds like an ordinary example of class inheritance to me, no need to
add complications with swapping out the __class__ for something different.


The only reason I would consider using the __class__ trick here is if you
don't control the instantiation of the object: you don't create your own
instance, you receive it pre-instantiated from elsewhere.




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list