Getting in to metaprogramming

L

Lawrence D'Oliveiro

Rafe said:
In the name of self-education can anyone share some pointers, links,
modules, etc that I might use to begin learning how to do some
"metaprogramming".

Fred Brooks, in his classic "Mythical Man-Month", defined "metaprogramming" as simply a very high-level form of programming, using a language designed to call entire programs as opposed to procedures/subroutines.

He quoted AppleScript as a good example (this was back in 1995), but Perl would have been just as good. Or these days, even better, Python.
 
M

Michele Simionato

Given that, can anybody think of an example that you could not do with
a class?  (excepting the "stored procedure" aspect)

The namedtuple recipe by Raymond Hettinger (http://
code.activestate.com/recipes/500261)
is an interesting example of code generation. My own decorator module
use a similar
trick. Here code generation (plus eval/exec) is needed since you need
control on
the signature of a function. If we could change the signature of a
function (I think
this is possible in Python 3.0, I have to check) then we would not
need code
generation.
 
T

Terry Reedy

Hendrik said:
I am not sure I understand the answer - if the input and output are both
bits of python source, then the output must be stored and evaluated or
imported to get it executed, right?

Yes. I would couple the above with

def iterfunc(rtext):
itext = iterize(rtext)
<code to exec itext to make function object>
return ifunc

The text transform is the hard part and should be separate for testing
and possible output for optimization or reuse elsewhere.

The scenario I have in mind is
1. write program with recursive function and test
2. triple-quote function text and wrap with function call; retest.

Yes, Steven's tail recursion example is example of above.
Now if this is the case, my question has to do with whether it is always
possible to put the equivalent code in a class, and instantiating it so that
it is either immediately executed or ready for immediate execution. My
gut feel tells me that it should always be possible, but I have often had
wrong hunches.

Not sure if this makes it any clearer.

I am not sure what you mean by 'do with a class'. A class is an active
namespace in that it creates and initializes instances and does a bit
extra with method calls, but it is otherwise a passive namespace container.

Any code can be put into class methods, but I do not see any reason to
do so for this example.

Terry Jan Reedy
 
K

Kay Schluehr

"Given that, can anybody think of an example that you could not do
with a class?"

Generating a template for a specific script application. For example,
a script with pre-defined callbacks that only require the addition of
the contents.

I was really interested in exploring the idea of using python output,
instead of XML, to record something a user did in a GUI. I have seen
it done and it is really advantageous in the 3D industry because it
means the script files can be edited directly, in a pinch, to generate
something slightly different.

For example, say we have code which generates a cube by plotting it's
points. A user then changes a point position in the GUI. The change is
saved by outputting the function call to a file with new arguments
(the new point location). If I wanted to make 100s of copies of the
cube, but with a slightly different point position, I could edit the
custom cube's python code and hand it back for creation without using
the GUI. I could do this with XML, but it can be harder to work with
in a text editor (though I have seen some XML editors that make it a
bit easier.) In fact, in most 3D applications, the app prints
everything the user does to a log. Sometimes in a choice of languages,
so I guess I am looking to do the same thing with my own custom tools.

In a real situation the generated code file can build some pretty
complex 3D object hierarchies. It moves beyond simple storage of data
and becomes a real script that can be hacked as necessary.

It is nice to have everything as python scripts because we always have
a blend of GUI users and developers to get a job done.

- Rafe

A rather advanced approach to deal with "writing XML in Python" and
generating "Python from XML" has been chosen in P4D:

http://pypi.python.org/pypi/P4D Langlet/1.2.4

It is of course a cultural mismatch to do such stuff because it
targets a proper superset of Python but I don't think it's worse than
what has been done in a plethora of template languages ( P4D is none
of them though ), YAML or related approaches. I did it mostly because
I wanted to learn Adobe Flex and I wanted to use it as a scripting
language, not as something I had to edit in a particular editor like
FlexBuilder.
 
S

Steven D'Aprano

I am not sure I understand the answer - if the input and output are both
bits of python source, then the output must be stored and evaluated or
imported to get it executed, right?

I think there's some confusion here. Python, like all general-purpose
programming languages, is able to process text. Python source code, like
that of almost all programming languages, is text.

Therefore Python can process Python source code as text: it can read it
and process it, or it can write it. There's nothing mysterious about
this, although how useful it is depends on the nature of the processing.

Once you have that Python source code, it is no different whether you
wrote it by hand, or wrote it with the aid of another program. To execute
it, you have to execute it.

Here's a trivial example of metaprogramming. Suppose I am interested in
the compilation time of Python code, rather than the execution time. I
might do this:

numitems = 100000
template = """def main():
# %s
%s%s
return L
"""

def make_file(name, comment, setup, body):
f = open(name, 'w')
f.write(template % (comment, setup, body))
f.close()

body = 'L.append(None)\n'
make_file('dumb.py', 'Create a big list the dumb way.',
'L = []\n', body*numitems)
make_file('smart.py', 'Create a big list the smart way.',
'', 'L = [None]*%d\n' % numitems)



At this point, I have two source files which I can do anything I like
with. It would have been a real pain to have written the first one by
hand, even with a clever editor. I can post-process them, run them
through other tools, even open them up in an editor and manipulate them
by hand (although that defeats the purpose of metaprogramming). Now I can
run the source files as input to another piece of code, and get a (very
rough) estimate of the compilation time:

from time import time
start = time()
execfile('dumb.py') # bypass the import mechanism
tdumb = time() - start
print "Time taken to compile dumb.py is %f seconds" % tdumb

start = time()
execfile('smart.py')
tsmart = time() - start
print "Time taken to compile smart.py is %f seconds" % tsmart



On my computer, I get:

Time taken to compile dumb.py is 2.236122 seconds
Time taken to compile smart.py is 0.003754 seconds
 
H

Hendrik van Rooyen

I am not sure I understand your question.

def iterize(recursive_function_text):
<code to parse input and fill a template>
return equivalent_iterative_function_text

where input and output are both Python code. If one were to implement
this by compiling the input to AST form and then walking the tree, the
AST node classes would be involved, but I would scarely say the
translation was done by the classes, as opposed to functions which might
or might not be attacked to a class as methods.

I am not sure I understand the answer - if the input and output are both
bits of python source, then the output must be stored and evaluated or
imported to get it executed, right?

Now if this is the case, my question has to do with whether it is always
possible to put the equivalent code in a class, and instantiating it so that
it is either immediately executed or ready for immediate execution. My
gut feel tells me that it should always be possible, but I have often had
wrong hunches.

Not sure if this makes it any clearer.

- Hendrik
 
H

Hendrik van Rooyen

Aaron Brady said:
folly...

The example I think of is the Visitor Pattern of Gamma and all. One
class's method calls another's method with its own class's name in the
name.

class Visitor:
def visit_A( self, arg ):...
def visit_B( self, arg ):...

class A:
def visit( self, vis ):
vis.visit_A( self )

class B:
def visit( self, vis ):
vis.visit_B( self )

As you can see, the 'visit' method is mechanical for classes A and B.
One might want to autogenerate those in some languages, but Python has
introspection:

class BaseAB:
def visit( self, vis ):
getattr( vis, 'visit_%s'% self.__class__.__name__ )( self )

And it's easier to modify the default behavior this way than in
autogenerated code too.

This is kind of the wrong way around - this is an example of
OO jiggery pokery that will be difficult to do by generating the
source code - are there things that can be done by generating
the source code that cannot be done with OO?

- Hendrik
 
A

alex23

Then another thing - it strikes me that any problem that can be solved
by metaprogramming, can be solved by putting similar code into a class
and instanciating an instance.

Does anybody know if this is true?

If it is, it limits the usefulness of metaprogramming to the creation
of "stored procedures" for "later execution".

Take a look at Pythoscope, which generates unit test stubs for a given
piece of Python code:

http://pythoscope.org/
http://pythoscope.org/test-generator
 
H

Hendrik van Rooyen

Steven D'Aprano said:
GUI designer. You write a program to let the user create code by clicking
buttons, dragging objects, drawing lines, etc. The GUI designer may use
classes, but the purpose of those classes is to generate source code.

Yikes, this is getting hairy- If "the problem" is to generate source code,
then you have to generate source code...
Testing code speed... you might have some functions with a loop, and you
want to unroll the loop as an optimization. If you have one function, you
can unroll it yourself. If you have a hundred such functions, you might
want to write a program to do it. (Yes, I'm stretching...)

Ok this one I'll buy - I can't think of a way to do this dynamically in a
class and get runnable code back. (but maybe I'm just not trying hard enough.)
Don't like that Python doesn't optimize tail-recursion? Then write a
source-code analyzer that detects tail-recursion and re-writes the
function using a while loop.

This is like TJR's example (I think)
It's been 20-odd years, and the examples were pretty trivial... I don't
really recall exactly, but it would have been something like this:

* design a GUI involving lots of buttons on screen, each one with quite
similar but not identical code;

* since Hypercard didn't have a layout manager, write a script to
generate each button, place it where needed, and set the button's code.

Hypercard did have a message passing hierarchy (like inheritance for
objects), so often you could take the button's code and place it in a
higher level of the hierarchy (the card, the background, the stack), but
there were odd cases where that wasn't enough.

Another example: Hypercard had a very limited number of GUI elements
(text fields and buttons, basically) but I designed a slider control
using a few buttons, each button with a custom script. To avoid needing
to create and place the buttons by hand each time I wanted a slider, I
had a script that did it for me. The script not only created the buttons,
but it created the scripts used by the buttons. This wasn't as difficult
as it sounds -- it was basically taking a template and doing some text
replacements, then telling the button to use it as a script.

Ok I think I am beginning to get the picture - when you have to do stuff
that the language does not directly support, then you use the simple
available elements, and create source code that strings them together
to make more complex stuff. -in this case its probably not trivial to
do it at run time.

The "make the source code" then run it, introduces a kind of compiler
stage.

For an old assembler programmer, this is starting to sound like macros.

So a different meta law would read like:

One uses Code Generation Techniques when the language does not
have macros.

*ducks*

- Hendrik
 
H

Hendrik van Rooyen

I just noticed that corepy 1.0 [1] has been released. Corepy is an
embedded DSL for synthesizing machine code from chaining Python
commands. This means it provides objects and exploits control
structures used to create machine code that can finally be executed
interactively.

Let's say you have an ordinary Python function that computes a CRC 32.
Now you could attempt to translate the function into other Python code
that expresses a corepy routine. You could create a decorator that
works as follows

1) reads the source of the decorated function
2) transforms the source into corepy source and compiles it or
3) if 2) fails it just returns the passed code object.

Kay

[1] http://www.corepy.org/

Thanks for the link.
This stuff sounds powerful - I will try to wrap my head around it.

- Hendrik
 
H

Hendrik van Rooyen

Michele Simionato said:
The namedtuple recipe by Raymond Hettinger (http://
code.activestate.com/recipes/500261)
is an interesting example of code generation. My own decorator module
use a similar
trick. Here code generation (plus eval/exec) is needed since you need
control on
the signature of a function. If we could change the signature of a
function (I think
this is possible in Python 3.0, I have to check) then we would not
need code
generation.

Right that is a definite counterexample.
van Rooyen's folly is officially dead.
It is stillborn.
It has snuffed it.
It lives no more.


Thanks.

- Hendrik
 
A

Aaron Brady

This is kind of the wrong way around - this is an example of
OO jiggery pokery that will be difficult to do by generating the
source code - are there things that can be done by generating
the source code that cannot be done with OO?

Yes, I was examining the special case of generating OO code.
 
A

Aaron Brady

On Nov 27, 8:21 pm, Steven D'Aprano <st...@REMOVE-THIS-
cybersource.com.au> wrote:
snip
body = 'L.append(None)\n'
make_file('dumb.py', 'Create a big list the dumb way.',
    'L = []\n', body*numitems)
make_file('smart.py', 'Create a big list the smart way.',
    '', 'L = [None]*%d\n' % numitems)

At this point, I have two source files which I can do anything I like
with. It would have been a real pain to have written the first one by
hand, even with a clever editor. I can post-process them, run them
through other tools, even open them up in an editor and manipulate them
by hand (although that defeats the purpose of metaprogramming).
snip

I tempt that one's metaprogram is in a metalanguage, and if it's
generating two programs, it has twice the power of the language it's
in, giving it a leverage of two units.

I feel like I always do that, manipulate the metaprogram output by
hand. I forget my use cases though, except for one that did low-level
structure handling.

P.S. tempt: '5. Obsolete. to try or test.' http://dictionary.reference.com/browse/tempt
 
E

Eric_Dexter

Yikes, this is getting hairy- If "the problem" is to generate source code,
then you have to generate source code...


Ok this one I'll buy - I can't think of a way to do this dynamically in a
class and get runnable code back. (but maybe I'm just not trying hard enough.)




This is like TJR's example (I think)











Ok I think I am beginning to get the picture - when you have to do stuff
that the language does not directly support, then you use the simple
available elements, and create source code that strings them together
to make more complex stuff. -in this case its probably not trivial to
do it at run time.

The "make the source code" then run it, introduces a kind of compiler
stage.

For an old assembler programmer, this is starting to sound like macros.

So a different meta law would read like:

One uses Code Generation Techniques when the language does not
have macros.

*ducks*

- Hendrik- Hide quoted text -

- Show quoted text -

I have been converting stuff like
sound 100, 1

exc... and just writing like
100, 1

for a number of languages and then just loading it into a spreadsheet
so that I can save little pieces of songs exc.. I can even use
different compilers or libraries that way.. I have started doing that
for quickbasic, qbasic, free basic, qb64, c++.... All I do is use
strings and '\n'.. I get to use a large number of older sounds and
effects (for basic) on newer compilers that have aditional options..
It just looks like another music tracker.. I am not sure if that fits
what you are trying to do though.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,470
Messages
2,571,809
Members
48,797
Latest member
PeterSimpson
Top