Re: Some python comments/questions

Mark Lutz (lutz@xvt.com)
Tue, 12 Oct 93 11:41:36 MDT

You write:

> 2) It would often be useful if there was some way to create function
> objects without binding them to some name. You can do it with
> something like
>
> def makefuncobj ():
> def tmp (x, y, z):
> return (x + y * z)
> return(tmp)
>
> a = makefuncobj()
>
> but you need to define a new makefuncobj for every function you
> wish to dynamically create, which takes some of the dynamism
> out of it :-). I'd like to be able to have some expression whihc
> evaluates to a function object without binding any names, perhaps
> something like
>
> a = def *(x, y, z):
> return x + y * z;
>
> You could almost do it with exec(), I'd think, except that exec()
> doesn't return its value. Are multi-line expressions even allowed?

If I understand your problem correctly, you can solve it with exec() and
strings. Things get a little tricky with multi-line function bodies (since
you need to remember to add '\n' and indentation in the strings) , but it
works. Consider the following 2 functions:

def genfunc(args, expr):
exec('def f(' + args + '): return ' + expr)
return eval('f')

def genproc(args, body):
exec('def f(' + args + '):' + body + '\n')
return eval('f')

genfunc() will create and return a new function that just returns the value
of an expression applied to arguments. For example:

x = genfunc('x, y', '(x * y) - (x + y)')
x(3, 4) -> 5

genproc() will build a function with a procedure body you pass in, which
can contain references to the symbols in the argument string:

x = genproc('i, j', '\n for x in range(i,j): print x,\n print')
x(0,10)

will print 0 1 2 ...9 and a new-line.

Note that "f" in both functions is a local variable, so you're not
really creating new symbols. You need to "return eval('f')", instead
of "return f" in my python version, since "f" is not known to be a
local variable statically (you could also do an initial "f = None",
to avoid having to "eval('f')"). You could also use the generated
function directly, but it's not too useful:

genfunc('x, y, z', 'x * y * x')(2, 4, 6) -> 48

Here's a test program that illustrates a bit more:

def test():
a, b = 3, 10
x = genfunc('x, y', 'x * 2 + y')
print x(2, 5) # 9
print x(a, b) # 16

y = genfunc('x, y, z', 'x * y * z')
print y(1,2,3) # 6
print y(a, b, 5) # 150
print x(10, a) # 23

z = genfunc('f, x', 'f(x, x) * 2')
print z(x, b) # 60

global msg; msg = 'it works.'
a = genproc('', 'print msg * 3')
a() # 'it works.' * 3
genproc('', 'print msg * 4')() # 'it works.' * 4

a = genproc('x, y', '\n for i in range(x,y): print i,\n print')

a(0,10) # 0..9
a(z(x,2), y(1,3,4)+5) # 12..16

b = genproc('f, m', 'f(0,10); print m')
b(a, 'all done.') # 0..9, 'all done.'

c = genproc('l, r', 'l; exec(r)')
c(a(0,10), 'print \'all done.\'') # ditto

d = genproc('l, r', 'exec(l); exec(r)')
d('for i in range(0,10): print i,', 'print \'all done.\'')

Mark Lutz
lutz@xvt.com