0

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?

3
  • 1
    You could try to do it with a search-and-replace in your editor. But note that the two might not always be equivalent. If the keyword arguments to getClass aren't passed in order, or if getClass accepts **kwargs, the second form may be invalid. Whether you can safely do this depends on the function signature. Commented Jul 23, 2013 at 17:57
  • 3
    Why is it desirable to do this? What is the problem here you're trying to solve? To quote the zen of python, "explicit is better than implicit". Commented Jul 23, 2013 at 18:19
  • @Eric Keyword arguments are often very useful for ensuring semantic clarity (especially for functions that take many arguments), but I have over-used them in some cases, and I want to remove the keyword arguments from several function calls, in order to maintain conciseness. (In other words, they're very useful for maintaining code readability, except when they are used excessively). Commented Jul 23, 2013 at 19:25

1 Answer 1

5

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.

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.