1

I'm trying to build a python class that parses a string and if it matches a regex that looks like a function call attempt to call that function on the class, passing in any parameters.

For example a string like "foo("a", 20")" would translate to something like self.foo("a", 20).

Here is the code that I have so far..

class FooTranslate(object):
    def create_string(self, letter, size):
        return letter*size

    def run_function(self, func_str):
        match = re.match("([\w_]+)\((|[\W\d\w\,]+)\)", func_str)
        if match == None:
            print "Couldn't match a regex!"
            return False
        else:
            func, fargs = match.groups()

        try:
            if fargs == "":
                return self.__getattribute__(func)()
            else:
                return self.__getattribute__(func)(eval(fargs))
        except AttributeError, e:
            print "Invalid function call: %s" % (func)
            return False

This code works in the basic cases...

In [1018]: foot = FooTranslate()
In [1019]: foot.run_function("foo()")
Foo!
In [1020]: foot.run_function("bar(2)")
FooFoo

However in the case of using 2 argument functions:

In [1021]: foot.run_function("create_string('a', 2)")

in run_function(self, func_str)
     24                 return self.__getattribute__(func)()
     25             else:
---> 26                 return self.__getattribute__(func)(eval(fargs))
     27         except AttributeError, e:
     28             print "Invalid function call: %s" % (func)

TypeError: create_string() takes exactly 3 arguments (2 given)

The reason why is that the eval() call returns fargs as a tuple, which create_string() takes as only a single argument. Any idea how I can pass a variable number of arguments through to a function call? Or have a better alternative way to do this?

1
  • Have a look at ast.literal_eval() as a safer alternative to eval. Commented Dec 23, 2011 at 13:08

1 Answer 1

1

You can use the * operator to explode a tuple into separate arguments to a function. For example:

def f(a, b, c):
    print a, b, c

If I call f(...) like this:

f((1,2,3))

I get an error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: f() takes exactly 3 arguments (1 given)

But if I call it like this:

f(*(1,2,3))

I get:

1 2 3

The * operator will even work if the function takes a variable number of arguments. For example, given the following function:

def f2(a, b, *args):
    print a, b,
    for x in args:
        print x,
    print

If I call f2(*(1,2,3,4,5)) it prints:

1 2 3 4 5
Sign up to request clarification or add additional context in comments.

1 Comment

I suspect this is the answer I'm looking for, thanks! If anyone has better methods of doing this (i.e. not using eval), that would be very useful to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.