How to dynamicly define function and call the function?

bruno modulix onurb at xiludom.gro
Fri Sep 9 08:37:38 EDT 2005


FAN wrote:
> I want to define some function in python script dynamicly and call
> them later, but I get some problem. I have tried the following:
> 
> ##################################
> # code
> ##################################
> class test:
>         def __init__(self):
>             exec("def dfunc(msg):\n\tprint msg\nprint 'exec def function'")
>             dfunc('Msg in init ...')   #  it work
> 			
>         def show(self, msg):
>              dfunc(msg) # doesn't work !
> I think this maybe cause by the scope of function definition, for the
> first call of 'dfunc' in __init__ work.

The exec statement syntax is:
"exec" expression  ["in" expression ["," expression]]

http://www.python.org/doc/2.4.1/ref/exec.html

Without the optional part, the default is to execute the statement in
the current scope. As Python allows defining nested functions, the
dfunc() function is local to the the __init__() function and doesn't
exists in the class scope or the global scope.

class Test2(object):
    def __init__(self):
        exec "def dfunc(msg):\n\tprint msg\nprint 'exec def function'" \
             in globals()
        dfunc('Msg in init ...')   #  it work

    def show(self, msg):
        dfunc(msg)

d2 = Test2()
d2.show('hello')

But I would not advise you to use exec this way...

> So I tried to define the
> function as a member function of class 'test', but this time even the
> first call doesn't work:
>
> ##################################
> # code
> ##################################
> class test:
>         def __init__(self):
>             exec("def dfunc(self,msg):\n\tprint msg\nprint 'exec def
function'")
>             self.dfunc('Msg in init ...')

Here again, for the function to become a method of the class, it has to
be defined in the scope of the class - not in the scope of the
__init__() function:

class Test(object):
    exec "def dfunc(self,msg):\n\tprint msg\nprint 'exec def function'"

    def __init__(self):
        self.dfunc('Msg in init ...')

			
    def show(self, msg):
        self.dfunc(msg)

d = Test()
d.show('hello')

> Is there any way I can solve this problem?

The first question that comes to mind is "aren't you trying to solve the
wrong problem ?". If you tell us more about your real use case, we may
point you to others - possibly better - ways of solving it. I don't mean
that it's wrong to use the exec statement, but my experience is that
I've never had a use case for it in 5+ years of Python programming.
Everytime I thought I needed exec, it turned out that there was a much
better solution, usually involving callables, closures, properties,
descriptors, metaclasses, or any combination of...

Also, the fact that you seems to be at lost with Python's inner
mechanisms makes me think you probably don't know other - possibly
better - ways to solve your problem.

If what you need is to "parameterize" a function, closures and nested
functions may be a better solution. A silly exemple:

def makeadder(step):
  def adder(num):
    return step + num
  return adder

add1 = makeadder(1)
add3 = makeadder(3)

add1(1)
=> 2
add1(2)
=> 3
add2(1)
=> 3
add2(2)
=> 4

Functions being first-class citizen in Python, it's easy to parameterize
a function with another:

import sys
def buildfun(funtest, funiftrue, funiffalse):
  def built(arg):
    if funtest(arg):
        print "%s is true for %s" % (funtest, arg)
        funiftrue("%s\n" % str(arg))
    else:
        print "%s is false for %s" % (funtest, arg)
        funiffalse("%s\n" % str(arg))
  return built

b = buildfun(lambda arg: arg == 42, sys.stdout.write, sys.stderr.write)
b(42)
b("toto")

Another possibility is to work with callable objects. A function in
Python is just an object (an instance of the function class), and any
object having a __call__() method is callable:

class Greeter(object):
   def __init__(self,
                name,
                greeting="hello %(who)s, my name is %(name)s"):

     """ greeting is supposed to be a format string
         with %(name)s and %(who)s in it
     """
     self.name = name
     self.greeting = greeting

   def __call__(self, who):
     return self.greeting % {'name': self.name, 'who': who}

Yet another possibility is to go with metaclasses, but I won't give an
exemple here !-)


HTH
-- 
bruno desthuilliers
python -c "print '@'.join(['.'.join([w[::-1] for w in p.split('.')]) for
p in 'onurb at xiludom.gro'.split('@')])"



More information about the Python-list mailing list