9

This is something that's bugged me for awhile now:

def test (*args, **kwargs):
    print target

test(foo='bar', target='baz')

I would presume that target='test' in the aFunc call at the bottom would end up in kwargs (and it does), and I would also presume that **would unpack kwargs in the function call, so target would exist as a keyword argument inside of aFunc. It doesn't. I know that it comes in as a dict, but I need to have that dict unpack in the argument list. Is this possible? In short, is there any way to have *args and **kwargs disappear and have the actual args and kwargs go into the call?

Edit: I threw together a case where unpacking of *args and **kwargs might help:

Let's say I have a function that prints a list:

def printList (inputList=None):
    print inputList

I want to be able to pass no list and have a default list supplied:

def ensureList (listFunc):
    def wrapper (inputList=None):
        listFunc(inputList=inputList or ['a','default','list'])
    return wrapper

@ensureList
def printList (inputList=None):
    print inputList

Now I want to get a bit more complicated with a list repeater:

@ensureList
def repeatList (inputList=None):
    print inputList*2

That works fine. But now I want variable repeating:

@ensureList
def repeatList (times, inputList=None):
    print inputList*times

Now you would be able to say:

repeatList(5)

It would generate the default list and repeat it 5 times.

This fails, of course, because wrapper can't handle the times argument. I could of course do this:

@ensureList
def repeatList (inputList=None, times=1)

But then I always have to do this:

repeatList(times=5)

And maybe in some cases I want to enforce supplying a value, so a non-keyword arg makes sense.

When I first encountered problems like this last year, I thought a simple solution would be to remove the requirements on the wrapper:

def ensureList (listFunc):
    "info here re: operating on/requiring an inputList keyword arg"
    def wrapper (*args, **kwargs):
        listFunc(inputList=inputList or ['a','default','list'])
    return wrapper

That doesn't work, though. This is why I'd like to have args and kwargs actually expand, or I'd like to have a way to do the expansion. Then whatever args and kwargs I supply, they actually fill in the arguments, and not a list and a dict. The documentation in the wrapper would explain requirements. If you pass in inputList, it would actually go in, and inputList in the call back to repeatList from the wrapper would be valid. If you didn't pass in inputList, it would create it in the call back to repeatList with a default list. If your function didn't care, but used *kwargs, it would just gracefully accept it without issue.

Apologies if any of the above is wrong (beyond the general concept). I typed it out in here, untested, and it's very late.

9
  • 6
    "I need to have that dict unpack in the argument list." – I cannot imagine any reason why you would need this. Could you please enlighten me? Commented May 23, 2012 at 10:06
  • 5
    If you know which options you're going to get, then why use **kwargs at all? Commented May 23, 2012 at 10:06
  • 3
    How to better go about doing what? You don't know what I'm doing. I just asked if I could unpack the list and dict inside the arguments. And it isn't complaining. It's just stating a fact. This site has a very difficult crowd. I ask straightforward questions - is it possible to do this? with a code example - and I always am met with people asking "Why on earth would you ever want to do that?" Maybe I don't. I just want to know if I can. Commented May 23, 2012 at 10:15
  • 3
    @GaryFixler: what you trying to achieve in the question makes no sense, so it's quite natural that people point that out. Commented May 23, 2012 at 10:19
  • 3
    @jamylak - you're coming across as very abusive. I've been working in Python for 3 years. I solve things all day with Python, but once in awhile I can't figure something out, like this simple thing - I've been told **dict unpacks the dict into key=value pairs, but it doesn't seem to actually work that way, and I've been unable to figure out more about it. The rare, weird things - the things I will probably never use - are the things that I ask here, because everything else I can solve on my own or through google. I don't mean to offend people. I'm trying to understand what's possible. Commented May 23, 2012 at 10:29

2 Answers 2

16

The answer to "why doesn't ** unpack kwargs in function calls?" is: Because it's a bad idea, the person who develop a function does not want local variable to just appear depending on the call arguments.

So, this is not how it's working and you surely do not want python to behave like that.

To access the target variable in the function, you can either use:

def test(target='<default-value>', *args, **kwargs):
    print target

or

def test(*args, **kwargs):
    target = kwargs.get('target', '<default-value>')
    print target

However, if you want a hack (educational usage only) to unpack **kwargs, you can try that:

def test(*args, **kwargs):
    for i in kwargs:
        exec('%s = %s' % (i, repr(kwargs[i])))
    print target
Sign up to request clarification or add additional context in comments.

9 Comments

Thank you for answering the question. It appears it is not possible.
It is possible, it's just a Bad Idea, like it is 99% of the time someone thinks they want to create variable names dynamically.
Good answer, but what do you mean by "*args can't be automatically unpacked because there is no variable name associated."
Thanks for the update, math. I definitely don't want to do that :)
That target = kwargs.get('target', '<default-value>') bit is perfect for use in Bottle/Flask type apps where you want to have optional keyword variables in a url
|
2

The obvious way for this particular case is

def test(foo=None, target=None):
    print target

test(foo='bar', target='baz')

If you want to access a parameter inside a function by name, name it explicitly in the argument list.

1 Comment

This doesn't answer the question.

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.