1

Possible Duplicate:
Python dynamic function creation with custom names

I have written a little script to determine whether what I wanted to do is possible. It is.

My goal is to dynamically (at runtime) create functions (or methods) which have names based on a list of arbitrary size (size of the list = number of functions dynamically created). All the functions do the same (for now), they just print their arguments.

The following code does exactly what I want, BUT, it is not clean and very brute-force. I'm trying to figure out if there is a better way to do this.

class Binder:
    def __init__(self, test_cases):
        """"
        test_cases: a list of function/method names.
        length of test_case = number of methods created.
        """

        for test_case in test_cases:
            #construct a code string for creating a new method using "exec" 
            func_str = "def "
            func_str += test_case
            func_str += "(*args):"
            func_str += "\n\t"
            func_str += "for arg in args:"
            func_str += "\n\t\t"
            func_str += "print arg"
            func_str += "\n"

            """
            For example, func_str for test_cases[0]= "func1" is simply:
            def func1(*args):
                for arg in args:
                    print arg
            """
            #use exec to define the function
            exec(func_str)

            #add the function as a method to this class
            # for test_cases[0] = "func1", this is: self.func1 = func1
            set_self = "self." + test_case + " = " + test_case
            exec(set_self)

if __name__ == '__main__':
    #this list holds the names of the new functions to be created
    test_cases = ["func1", "func2", "func3", "func4"]
    b = Binder(test_cases)

    #simply call each function as the instant's attributes
    b.func1(1)
    b.func2(1, 3, 5)
    b.func4(10)

Output is:

1
1
3
5
10

As expected.

update the content of the function would not simply be a for loop printing the args, it would do something more meaningful. I get the exact result I want from the piece of code above, just wondering if there is a better way of doing it.

update I'm tying two ends of a much bigger module. One end determines what the test cases are and among other things, populates a list of the test cases' names. The other end is the functions themselves, which must have 1:1 mapping with the name of the test case. So I have the name of the test cases, I know what I want to do with each test case, I just need to create the functions that have the name of the test cases. Since the name of the test cases are determined at runtime, the function creation based on those test cases must be at runtime as well. The number of test cases is also determined at runtime.

Is there a better way to do this?? Any and all suggestions welcome.

Thanks in advance.

Mahdi

Community
  • 1
  • 1
mahdiolfat
  • 821
  • 1
  • 9
  • 12
  • 3
    Hmmm, I'm sensing that you're starting on the wrong foot here. Why would you want to do that in the first place? – rantanplan Nov 02 '12 at 16:31
  • What for are you doing this? Why do you need to generate these functions? – piokuc Nov 02 '12 at 16:32
  • @rantanplan. I definitely need to do this. This piece of code would be part of a much bigger module, where I need to create custom named functions as they refer to test cases which are called from another module. All determined at runtime. – mahdiolfat Nov 02 '12 at 16:34
  • Also, the content of the function would not simply be a for loop printing the args, it would do something more meaningful (run the test case). I get the exact result I want from the piece of code above, just wondering if there is a better way of doing it. – mahdiolfat Nov 02 '12 at 16:36
  • 3
    Dynamically bind a name I can understand. Dynamically binding to an object I understand. But dynamically generating code? Why not define the functions in the first place and just put them in a file? – rantanplan Nov 02 '12 at 16:37
  • @mahdiolfat what are you trying to do? If you tell us what you're trying to do we can help you – Aniket Inge Nov 02 '12 at 16:40
  • @rantanplan. Because I wouldn't know how many functions to define and what their names would have to be. That is all determined at runtime. I'm trying to tie two ends here. One end determines the test cases that need to be run, and among other things, it creates a list with the name of test cases. The other end are the functions which actually exercise the test cases. The functions MUST have the same name as the test case, hence, the piece of code I have here. Now, the name and the number of test cases is ALL determined at runtime. – mahdiolfat Nov 02 '12 at 16:40
  • if you use Python, why not create a class and use the __getattr__ function to catch **any** method call (and then decide depending on the name what to do). Here is an example of this function in use: http://code.activestate.com/recipes/307618-class-with-default-method-handler/ – NoUsername Nov 02 '12 at 16:49
  • You can dynamically call on runtime any function you like(e.g. getattr(myobj, 'foo_func')() ). You can dynamically change the name of any object you want. But generating code on runtime is something that I can't find a reason for or that I have ever seen, anywhere. – rantanplan Nov 02 '12 at 16:49
  • @rantanplan: great! would you please make these suggestions into an answer. Thanks. – mahdiolfat Nov 02 '12 at 16:55
  • @rantanplan, any suggestions on how to dynamically create a function not just dynamically call it? Your answer assumes the object/function already exists. – mahdiolfat Nov 02 '12 at 17:01
  • @mahdiolfat; You're obsessed with dynamically creating functions rather than solving your problem. – martineau Nov 02 '12 at 17:05
  • You can dynamically create a function compiling the body and calling `types.FunctionType`, instead of using `exec`. This should be a bit safer than using plain `exec`. Anyway if you only need the name to be dynamic you can simply set the function's name with `function.__name__ = "new name"` By the way, I think you're not going to solve the problem using dynamically created functions. – Bakuriu Nov 02 '12 at 17:05
  • Yes I assume the body of the function exists. I don't know if you can do that or if there is a reason for it. The answers below sum it up pretty good I think. – rantanplan Nov 02 '12 at 18:17
  • I disagree that these two posts are duplicates. Here I asked a very specific question about a piece of code I presented. This is what I really wanted to do and did it and wanted to see if people had alternatives. In the other post I just described the problem and asked for suggestions. I do agree the title of the questions are very similar, but the content different. – mahdiolfat Nov 03 '12 at 14:31

2 Answers2

1

In Python this is the most reasonable approach for generic metaprogramming.

If you need just some constants in the code then however a closure may do the trick... for example:

def multiplier(k):
    "Returns a function that multiplies the argument by k"
    def f(x):
        return x*k
    return f

For arbitrary code generation Python has an ast module and you can in theory both inspect or create functions using that approach. However in Python code is hard to represent and manipulate that way so normally the approach is to just do everything at runtime instead of compiling specific functions. When you really can get an advantage you can use eval (to get a lambda) or exec. The ast module is used mostly just for inspection.

Writing code that generates code is what is called metaprogramming and Python is not very friendly to this approach.

You may have heard that C++ claims support for metaprogramming, but indeed it's only template-based metaprogramming where you can substitute types or constants into a fixed structure. You can play some tricks using "advanced metaprogramming" like the SFINAE rule but they're nothing more than trickery.

Python doesn't need templates because the language is not statically typed and you have closures (C++ is statically typed and there are no closures), but Python doesn't help with a general metaprogramming approach (i.e. writing general code that generates or manipulates code).

If you're interested in metaprogramming then the language of choice is probably Common Lisp. Writing code that writes code is not something special for Lisp and while it's of course more difficult to write a macro (a function that writes code) than a regular run-time function still with Lisp the difficulties are mostly essential (the problem is indeed harder) and not artificial because of language limitations.

There's an old joke among lispers that goes more or less like "I'm writing code that writes code that writes code that writes code that people pays me for". Metaprogramming is indeed just the first step, after that you have meta-meta-programming (writing code that writes code generators) and so on. Of course things gets harder and harder (but once again because the problem is harder, not because of arbitrarily imposed limitations...).

6502
  • 112,025
  • 15
  • 165
  • 265
1

First of all, this is probably a really bad idea. To see why, read the comments and the other answers.

But to answer your question, this should work (although it's a hack and might have strange side effects):

from types import MethodType

def myfunc(self, *args):
    for arg in args:
        print arg

class Binder(object):
    def __init__(self, test_cases):
        for test_case in test_cases:
            method = MethodType(myfunc, self, Binder)
            setattr(self, test_case, method)

In this case, the reference to the function is hardcoded, but you can of course pass it as an argument too.

This is the output:

>>> b = Binder(['a', 'b'])
>>> b.a(1, 2, 3)
1
2
3
>>> b.b('a', 20, None)
a
20
None
Danilo Bargen
  • 18,626
  • 15
  • 91
  • 127