Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid boilerplate when using super(...) in Python 2.6+?

Are there good (suitable for using in real projects) ways or reducing boilerplate in things like this

class B(A):
    def qqq(self): # 1 unwanted token "self"
        super(B, self).qqq() # 7 unwanted tokens plus 2 duplications ("B", "qqq")
        do_something()

I want it to look more like this:

class B(A):
    def qqq:
        super
        do_something()

or (more realistically)

class B(A):
    @autosuper_before
    def qqq(self):
        do_something()

Is it possible in Python 2.6+ without overt hacks?

@link super() in Python 2.x without args

like image 209
Vi. Avatar asked Oct 08 '22 00:10

Vi.


1 Answers

tl;dr

As the OP said "Is it possible in Python 2.6+ without overt hacks?", the answer is: No

Long version

You can make a simple decorator that will call the next parent with this method. The problem is that you will not have control on the arguments you want to pass.

Edit: This will not work for a subclass already using autosuper because it'll chose the wrong class and make an infinite loop.

def autosuper(fn):
    def f(self, *args, **kw):
        cl = super(type(self), self)
        getattr(cl, fn.__name__)(*args, **kw)
        return fn(self, *args, **kw)
    return f

How could this be done? Python 3.x do have a super function that takes no arguments!

Unfortunally, the Python 3.x's super is a class and at the same time a keyword, because just the presence of its name will change the current environment to unveil a variable named __class__ that is the right class you need to use!

If you check the frame inside a function declared in a class, there's no __class__ variable and the co_freevars attribute of the frame's f_code attribute is empty. When you write the name super (do not need to call it), the __class__ string will appear in co_freevars meaning it comes from another closure. Also, if you try to access the __class__ variable without using super, it'll use the LOAD_DEFER bytecode for this same reason instead of LOAD_GLOBAL like would be normal to every undefined name.

This is so crazy that you cannot just do hyper = super and call this new hyper variable without arguments (that is exactly the same object as super).

As I cannot compete with this much of black magic inside the Python Interpreter, and because the autosuper decorator is not declared inside a class (so it can never access the __class__ variable even if that was possible in Python 2.x), I will not try to write a new decorator and will leave this answer here as a warn for other people who want to do that.

It is probably possible to make some hackeries to find the right class to use, but I will not dig that far. Things to consider:

  • When the decorator is applied, the class do not exist yet, so this should be done when the decorated function is called.
  • The function being decorated is not yet an unbound method (that were removed anyway from Py3k) so you cannot check the im_class attribute.
  • The frame does not seem to hold any information of the class used to make this call (unless of course the __class__ variable do exist and it is possible to get a reference to it)
  • This answer provided by OP is also quite broken because it makes a lot of bad assumptions and has problems with decorated functions.
like image 97
JBernardo Avatar answered Oct 13 '22 11:10

JBernardo