Anderson Green
Anderson Green

Reputation: 31840

Automatically remove named parameters from a Python function call

I've written a Python library that uses a lot of named parameters in its function calls, and I'm wondering if there's any way to automatically remove all of these named parameters, instead of removing them manually (which is a very tedious task).

For example, would it be possible to translate this function call:

getClass(lang="Java", body=[
    mainMethod(lang="Java", body=[
        println(lang="Java", toPrint="Hello World")
    ])
])

into the function call below (which omits the named arguments, and is much more concise?):

getClass("Java", [
    mainMethod("Java", [
        println("Java", "Hello World!")
    ])
])

In order to do this, one possible approach would be to write a function that would print its own function call as a string - however, I don't think it's possible to write a function that would do this. Are there any other approaches to this problem that might work as well?

Upvotes: 0

Views: 1865

Answers (1)

2rs2ts
2rs2ts

Reputation: 11046

There is no need. Keyword arguments can still be passed positionally. There is the added benefit, here, of being able to specify one or none of them, if you would so please. However, you don't need to specify any at all.

>>> def foo(bar=1, baz=[2,3]):
...     print bar, baz
... 
>>> foo()
1 [2, 3]
>>> foo(baz=4)
1 4
>>> foo(10, 20)
10 20

If I'm misunderstanding the code you're providing, let me know. Steve's answer seems to indicate that you are actually working with strings, but I didn't see anything in your post to indicate that.

You did mention a function which prints its own function call; by this, I assume you mean that the function must print a string which looks exactly like the thing you'd type to call the function with the same arguments which you passed. This is relatively easy to do, because you can either type the function's name as-is, or you can use its __name__ attribute.

>>> def goo(a,b):
...     print "{}({}, {})".format(goo.__name__, a, b)
... 
>>> goo(1,2)
goo(1, 2)
>>> def hoo(*args):
...     print "{}({})".format(hoo.__name__, ', '.join((str(arg) for arg in args)))
... 
>>> 
>>> hoo(2,3,4,5)
hoo(2, 3, 4, 5)

Thinking about it, your example seems wanting of a general function which would grant this behavior to any function - recursively. Here's a way to achieve that, using partials (I'll redefine foo() so that the example makes some more sense):

>>> from functools import partial
>>> def foo(a, b):                                                              
...     return (a if not isinstance(a, partial) else a()) + (b if not isinstance(b, partial) else b())
... 
>>> fun = partial(foo, 1, partial(foo, partial(foo, 2, 4), partial(foo, 3, 5)))
>>> fun()
15
>>> fun = partial(foo, 1, partial(foo, partial(foo, 2, 4), partial(foo, 3, 5)))
>>> def print_pfunc(pfunc):
...     return "{}({}{}{})".format(pfunc.func.__name__, ', '.join(str(arg) if not isinstance(arg, partial) else print_pfunc(arg) for arg in pfunc.args) if pfunc.args else '', ', ' if pfunc.args and pfunc.keywords else '', ', '.join('{}={}'.format(k, v if not isinstance(v, partial) else print_pfunc(v)) for k, v in pfunc.keywords) if pfunc.keywords else '')
... 
>>> print print_pfunc(fun)
foo(1, foo(foo(2, 4), foo(3, 5)))

If you're not a fan of that very long format() call, here's a different way to write it (just so that you don't have to spend time decoding my garbage):

def print_pfunc(pfunc):
    args = ""
    if pfunc.args is not None:
        args = ', '.join(str(arg) if not isinstance(arg, partial) else print_pfunc(arg) for arg in pfunc.args)
    kwargs = ""
    if pfunc.keywords is not None:
        kwargs = ', '.join('{}={}'.format(k, v if not isinstance(v, partial) else print_pfunc(v)) for k, v in pfunc.keywords)
    return "{}({}{}{})".format(pfunc.func.__name__, args, ', ' if args and kwargs else '', kwargs)

Adapting this to your code, now, will require you to write code which will turn your function calls into partials before evaluating them. It's up to you what you want to do from this point - I can't think of a clever way to get around the fact that function calls passed as arguments get evaluated before they are passed, as this will interfere with what you are trying to do.

Upvotes: 5

Related Questions