super() in injected methods

Chris Angelico rosuav at gmail.com
Thu Feb 11 14:08:25 EST 2021


On Fri, Feb 12, 2021 at 5:54 AM Andras Tantos
<python-list at andras.tantosonline.com> wrote:
>
> Esteemed Python Gurus,
>
> I think, I actually know the answer to this question, but - maybe beyond
> reason - I'm hoping there to be some magic. Consider the following code:
>
>      from types import MethodType
>
>      class A(object):
>          pass
>          def m(self, x):
>              print(f"A.m({x})")
>      class B(A):
>          def m(self, x):
>              print(f"B.m({x})")
>              ss = super()
>              ss.m(x)
>
>      def method(self, s):
>          print(f"method({s})")
>          try:
>              ss = super() # <-- Complains about __class__ cell not being
> found
>          except:
>              print("I shouldn't need to do this!")
>              ss = super(type(self), self) # <-- Works just fine
>          ss.m(s)
>
>      a = B()
>      a.m(41)
>      a.m = MethodType(method, a)
>      a.m(42)
>
>
> In the function 'method', I try to access the super() class. Now, of
> course that makes no sense as a stand-alone function, but it does, once
> it gets injected as a method into 'a' below.
>
> The two-parameter version of the call of course works without a hitch.

Be careful: the first parameter is supposed to be the class that
you're currently implementing, which may well NOT be type(self). Given
that you're attaching to an instance, though, it's probably okay to
assume you are looking at the leaf class.

> I think I actually understand why this is happening (some interpreter
> magic around super() forcing the insertion of __class__, which that
> doesn't happen when parsing a stand-alone function). I think I even
> understand the rationale for it, which is that super() needs to be
> statically evaluated.

What happens in the normal case is that __class__ is accessed via
closure cell from the class block itself. The compiler translates
super() into super(__class__, self) where 'self' is actually 'whatever
the first parameter is'.

> Now to the question though: In theory this information (static type of
> 'self' at the point of method binding to class) is available at the
> point of method injection, in this example, the next-to-last line of the
> code. So, is there a way to somehow
> inject/override/magically-make-it-appear the __class__ cell in 'method'
> such that super() starts working as expected again?
>

Hmm. I don't think it'd work for technical reasons, but in theory, the
MethodType constructor would be the place to do this. But injecting
methods into instances (as opposed to classes) is a sufficiently
unusual thing that it's probably safest to just use the two-arg super
and have done with it.

ChrisA


More information about the Python-list mailing list