> Python classes may be clunky but it is said that Python class system is more flexible than other languages' OOP, even Lisp CLOS.<p>Nah. Python's metaprotocol is <i>inflexible</i> compared to other similarly-dynamic languages.<p>Here, let's try to monkeypatch a method onto a hierarchy of existing classes, calling the superclass method from time to time.<p>First, here's our existing hierarchy:<p><pre><code> class Base: pass
class Derived(Base): pass
</code></pre>
This is the decorator that will do the monkeypatching. Any other way of doing the monkeypatching will fall foul of the error we're about to see. There's no way around it.<p><pre><code> def extend(cls):
def extender(f):
setattr(cls, f.__name__, f)
return f
return extender
</code></pre>
OK, let's add `foo` to Base:<p><pre><code> @extend(Base)
def foo(self):
print('I am a base!')
</code></pre>
and to Derived, calling the superclass:<p><pre><code> @extend(Derived)
def foo(self):
print('I am a derived!')
super().foo()
print('I am still a derived!')
</code></pre>
Finally, let's try it:<p><pre><code> Derived().foo()
</code></pre>
Oh! What's this??<p><pre><code> ~$ python t.py
I am a derived!
Traceback (most recent call last):
File "/home/tonyg/t.py", line 20, in <module>
Derived().foo()
File "/home/tonyg/t.py", line 17, in foo
super().foo()
RuntimeError: super(): __class__ cell not found
</code></pre>
Huh!<p>---<p>Turns out in situations like this you have to hold the runtime's hand by supplying `super(Derived, self)` instead of `super()` in the `foo` in Derived. It's to do with how the compiler statically (!) assigns information about the superclass hierarchy using magic closure variables at compile-time (!).<p>There <i>may</i> be some insane magic tricks one could do to make this work, <i>maybe</i>. Things like reaching into a closure, rebuilding it, adding new slots, preserving existing bindings, avoiding accidental capture, making sure everything lines up just right. It... didn't seem like a good idea to put insane magic in production, so I did the traditional Python thing: swallowed my discomfort and pressed on with the stupid workaround for the bad design in order to accomplish something like what I was trying to achieve.