Descriptor/Decorator challenge

Michele Simionato michele.simionato at gmail.com
Thu Mar 8 09:37:39 CET 2007


Raymond Hettinger wrote:
> Any trick in the book (metaclasses, descriptors, etc) is fair game.

So you are asking for a serious hack, right?

As soon as I saw your challenge I thought "That's difficult. Very
difficult.
No way I can solve that with a simple descriptor/decorator. I need
more POWER.
Time to look at the byteplay module".

The byteplay module by Noam Raphael (http://byteplay.googlecode.com/
svn/trunk/byteplay.py) seems to exist just to make possible
spectacular hacks. So I took
your challenge as an opportunity to explore a bit its secrets. In
doing so,
I have also decided to break the rules and not to solve your problem,
but
a different one, which is the one I am more interested in ;)

Basically, I am happy with the double underscores, but I am unhappy
with
the fact that a method does not know the class where it is defined, so
that you have to repeat the name of the class in ``super``.

With some bytecode + metaclass hackery it is possible to make the
methods
smart enough to recognize the class where they are defined, so your
example could be solved as follows:

from currentclass import CurrentClassEnabled

class A(CurrentClassEnabled):
    def m(self):
        print 'A.m'
    def am(self):
        CurrentClass.m(self) # the same as A.m(self)

class B(A):
    def m(self):
        print 'B.m'
    def bm(self):
        CurrentClass.m(self) # the same as B.m(self)
    def superm(self):
        super(CurrentClass, self).m() # the same as super(B, self).m()

m = B()
m.am()     # prints 'A.m'
m.bm()     # prints 'B.m'
m.superm() # prints 'A.m'

As you see, as a byproduct, double underscores are not needed anymore,
since methods are called directly from the CurrentClass. The approach
also works for ordinary attributes which are not methods.

The code to enable recognition of CurrentClass is short enough to be
includede here, but I will qualify it as a five star-level hackery:

$ cat currentclass.py
# requires byteplay by Noam Raphael
# see http://byteplay.googlecode.com/svn/trunk/byteplay.py
from types import FunctionType
from byteplay import Code, LOAD_GLOBAL, STORE_FAST, LOAD_FAST

def addlocalvar(f, locname, globname):
    if locname not in f.func_code.co_names:
        return f # do nothing
    c = Code.from_code(f.func_code)
    c.code[1:1] = [(LOAD_GLOBAL, globname), (STORE_FAST, locname)]
    for i, (opcode, value) in enumerate(c.code[2:]):
        if opcode == LOAD_GLOBAL and value == locname:
            c.code[i+2] = (LOAD_FAST, locname)
    f.func_code = c.to_code()
    return f

class _CurrentClassEnabled(type):
    def __new__(mcl, name, bases, dic):
        for n, v in dic.iteritems():
            if isinstance(v, FunctionType):
                dic[n] = addlocalvar(v, 'CurrentClass', name)
        return super(_CurrentClassEnabled, mcl).__new__(mcl, name,
bases, dic)

class CurrentClassEnabled:
    __metaclass__ = _CurrentClassEnabled


Enjoy!

                  Michele Simionato




More information about the Python-list mailing list