Dynamic/runtime method creation (code generation) in Python Dynamic/runtime method creation (code generation) in Python python python

Dynamic/runtime method creation (code generation) in Python


Based on Theran's code, but extending it to methods on classes:

class Dynamo(object):    passdef add_dynamo(cls,i):    def innerdynamo(self):        print "in dynamo %d" % i    innerdynamo.__doc__ = "docstring for dynamo%d" % i    innerdynamo.__name__ = "dynamo%d" % i    setattr(cls,innerdynamo.__name__,innerdynamo)for i in range(2):    add_dynamo(Dynamo, i)d=Dynamo()d.dynamo0()d.dynamo1()

Which should print:

in dynamo 0in dynamo 1


Function docstrings and names are mutable properties. You can do anything you want in the inner function, or even have multiple versions of the inner function that makedynamo() chooses between. No need to build any code out of strings.

Here's a snippet out of the interpreter:

>>> def makedynamo(i):...     def innerdynamo():...         print "in dynamo %d" % i...     innerdynamo.__doc__ = "docstring for dynamo%d" % i...     innerdynamo.__name__ = "dynamo%d" % i...     return innerdynamo>>> dynamo10 = makedynamo(10)>>> help(dynamo10)Help on function dynamo10 in module __main__:dynamo10()    docstring for dynamo10


Python will let you declare a function in a function, so you don't have to do the exec trickery.

def __init__(self):    def dynamo(self, arg):        """ dynamo's a dynamic method!        """        self.weight += 1        return arg * self.weight    self.weight = 50    setattr(self.__class__, 'dynamo', dynamo)

If you want to have several versions of the function, you can put all of this in a loop and vary what you name them in the setattr function:

def __init__(self):    for i in range(0,10):        def dynamo(self, arg, i=i):            """ dynamo's a dynamic method!            """            self.weight += i            return arg * self.weight        setattr(self.__class__, 'dynamo_'+i, dynamo)        self.weight = 50

(I know this isn't great code, but it gets the point across). As far as setting the docstring, I know that's possible but I'd have to look it up in the documentation.

Edit: You can set the docstring via dynamo.__doc__, so you could do something like this in your loop body:

dynamo.__doc__ = "Adds %s to the weight" % i

Another Edit: With help from @eliben and @bobince, the closure problem should be solved.