Generic constructors and duplication of internal Python logic
Peter Otten
__peter__ at web.de
Thu Apr 29 12:32:12 EDT 2004
Michele Simionato wrote:
> sdementen at hotmail.com (Sebastien de Menten) wrote in message
> news:<8dad5312.0404280217.21c2cfd1 at posting.google.com>...
>> Here is a metaclass for uber-lazy user :-)
>>
>> Concretely, at the creation of the class it takes the source of the
>> __init__ function and add, at the first line of __init__, the line
>> that sets the attributes :
>> > self.foo, self.bar, self.baz, self.optional1, self.optional2 =
>> > foo, bar, baz, optional1, optional2
>>
>
> Unfortunately this approach does not work if the source is not available
> (this happens when you are in the interpreter, or when you only have a
> .pyc file). I was playing this kind of tricks some time ago, then I
> decided that it was best to switch to Lisp/Scheme for this kind of stuff
> ;)
Here's a variant that operates on the byte code:
import opcode
class List(list):
def ensure(self, value):
try:
return self.index(value)
except ValueError:
self.append(value)
return len(self)-1
class Recorder(object):
def __init__(self, code):
self.func_code = code
self._code = map(ord, code.co_code)[:-4]
self._names = List(code.co_names)
def __getattr__(self, name):
opc = opcode.opmap[name.upper()]
def record(self, arg=None):
# XXX limit name resolution/addition to the proper opcodes
if isinstance(arg, str):
arg = self._names.ensure(arg)
self._code.append(opc)
if arg is not None:
self._code.append(arg & 0xff)
self._code.append(arg >> 8)
setattr(self.__class__, name, record)
return getattr(self, name)
def code(self):
return ''.join(map(chr, self._code))
def names(self):
return tuple(self._names)
def autoinit(f):
co = f.func_code
r = Recorder(co)
for i in range(1, co.co_argcount):
r.load_fast(i)
r.load_fast(0) # self
r.store_attr(co.co_varnames[i])
r.load_const(0) # None
r.return_value()
new_names = r.names()
new_code = r.code()
codeobj = type(co)(co.co_argcount, co.co_nlocals, co.co_stacksize,
co.co_flags, new_code, co.co_consts, new_names,
co.co_varnames, co.co_filename, co.co_name,
co.co_firstlineno, co.co_lnotab, co.co_freevars,
co.co_cellvars)
return type(f)(codeobj, f.func_globals, f.func_name, f.func_defaults,
f.func_closure)
class AutoInit(type):
def __new__(cls, classname, bases, classdict):
classdict["__init__"] = autoinit(classdict["__init__"])
return type.__new__(cls, classname, bases, classdict)
class Demo(object):
__metaclass__ = AutoInit
def __init__(self, baz, top, foo=3, r=None):
if r is None:
r = ["default"]
foo *= 2
baz *= 3
helper = 42 #ignored
def __str__(self):
return ("Demo(baz=%(baz)r, top=%(top)r, foo=%(foo)r, r=%(r)r)"
% self.__dict__)
if __name__ == "__main__":
print Demo(1, 2)
print Demo(10, 20, 30, r=["other"])
print Demo(100, foo="other", top=200)
I guess that was just a complicated way to fail the sanity check :)
Peter
More information about the Python-list
mailing list