Limits of Metaprogramming

castironpi castironpi at gmail.com
Mon Aug 4 22:23:43 CEST 2008


On Aug 4, 1:57 pm, Wilson <PaulAlexWil... at gmail.com> wrote:
> On Aug 4, 6:49 pm, castironpi <castiro... at gmail.com> wrote:
>
> > Two, if all your methods will have uniform signatures and closures,
> > you can store class methods as only their co_code objects:
>
> > >>> C.g.im_func.func_code.co_code
>
> > 'd\x00\x00S'
>
> > And fabricate them dynamically into full live types as needed.
>
> Thanks for your comments and advice. This second option intrigues me;
> could you elaborate further, I don't follow you...
>
> Thanks Paul

Depending on the complexity of the functions, a code string could be
all you need to store to determine (redetermine) a function's
behavior.  For something moderately simple,

def trans1( self, prev, trans ):
	if prev== 0 and trans== 'a':
		return 1
	if prev== 1 and trans== 'b':
		return 0
	return prev

I found you need to store code.co_nlocals, code.co_code, and
code.co_consts, to distinguish from a blank stub.  With extra
variables, I needed code.co_names and code.co_varnames too.  To
recreate a code object completely, you need 12 variables (14 to
include closures), some of which are composite objects and would need
to be pickled to be stored.

Then you can build a new code object, then a new function object, then
a new method object, then you can call it.  Instead of a module of
code, perhaps you could have a datafile containing only these values,
up to twelve per record, one record for each different function you
have.

Here is the benefit:

newcode= dupecode( oldcode, codet1 )
newfun= FunctionType( newcode, {} )
stub.stub= MethodType( newfun, stub )
prev= stub.stub( prev, trans )
print prev

You can loop over these five lines, re-loading function data with
'dupecode', executing it, then reloading the next one, and you have a
different function.  Additions to your database of functions would
start in source first (unless you construct each parameter, in
particular co_code, by hand, which you may want), then get compiled,
then go in.

Here is the complete constructor for a code object:

>>> help(_)
Help on code object:

class code(object)
 |  code(argcount, nlocals, stacksize, flags, codestring, constants,
names,
 |        varnames, filename, name, firstlineno, lnotab[, freevars[,
cellvars]])

Here's the constructor in Python 3.0:

class code(object)
 |  code(argcount, kwonlyargcount nlocals, stacksize, flags,
codestring,
 |        constants, names, varnames, filename, name, firstlineno,
 |        lnotab[, freevars[, cellvars]])

I defined Stub.stub like this:

class Stub:
	def stub( self, prev, trans ):
		return prev
stub= Stub( )

You need imports from the types module:

from types import MethodType, FunctionType, CodeType

And here is 'dupecode', which currently only replaces five of the old
function's members with new ones:

def dupecode( old, new ):

	newcode= CodeType( old.co_argcount, new.co_nlocals, old.co_stacksize,
		old.co_flags,
		new.co_code,
		new.co_consts, new.co_names,
		new.co_varnames, old.co_filename, old.co_name, old.co_firstlineno,
		old.co_lnotab )

	return newcode



More information about the Python-list mailing list