newbie: self.member syntax seems /really/ annoying

Michele Simionato michele.simionato at gmail.com
Thu Sep 13 01:08:56 EDT 2007


On Sep 12, 1:05 pm, al... at mac.com (Alex Martelli) wrote:
> Making, say, 'a' hiddenly mean 'x.a', within a function, requires a
> decorator that suitably rewrites the function's bytecode... (after
> which, it WOULD still be terrible and horrible and not to be used, just
> as you say, but it might at least _work_;-).  Main problem is, the
> decorator needs to know the set of names to be "faked out" in this
> terrible and horrible way at the time the 'def' statement executes: it
> can't wait until runtime (to dynamically determine what's in var(self))
> before it rewrites the bytecode

All right Alex, and since I had 20 minutes of spare
time I implemented the decorator you are talking about by
using the wonderful byteplay module ;) Here it is:


import dis
from byteplay import Code, LOAD_GLOBAL, LOAD_ATTR, LOAD_FAST,
STORE_FAST

def instance2local(varname):
    yield LOAD_FAST, 'self'
    yield LOAD_ATTR, varname
    yield STORE_FAST, varname

def declare_instance_vars(*varnames):
    def dec(f):
        c = Code.from_code(f.func_code)
        # change LOAD_GLOBAL -> LOAD_FAST
        for i, (opcode, value) in enumerate(c.code):
            if opcode == LOAD_GLOBAL and value in varnames:
                c.code[i] = (LOAD_FAST, value)
        # insert instance2local assigments at the beginning
        assignments = []
        for varname in varnames:
            for pair in instance2local(varname):
                assignments.append(pair)
        c.code[0:0] = assignments
        # redefine the code object
        f.func_code = c.to_code()
        return f
    return dec

class Test(object):
    def __init__(self):
        self.a = 1
        self.b = 2

    def test1(self):
        a = self.a
        b = self.b
        return a * b

    @declare_instance_vars('a', 'b')
    def test2(self):
        return a * b


co1 = Test.__dict__['test1'].func_code
co2 = Test.__dict__['test2'].func_code

print 'bytecode for test1'
dis.dis(co1)
print 'bytecode for test2'
dis.dis(co2)

t = Test()
assert t.test1() == t.test2()

It is still a hack, since one is not supposed to mess around
with bytecodes, but at least it seems to work ;) [warning:
I have not tested it more than you see]

        Michele Simionato




More information about the Python-list mailing list