call f(a, *b) with f(*a, **b) ?

B

bukzor

This question seems easy but I can't figure it out.
Lets say there's a function:

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

and elsewhere in your program you have a list and a dict like this:
args = [2, 3]
kwargs = {'a':1}

I'd like to get f() to print something like the following, but I can't
figure out how.
1
2
 
I

inhahe

bukzor said:
This question seems easy but I can't figure it out.
Lets say there's a function:

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

and elsewhere in your program you have a list and a dict like this:
args = [2, 3]
kwargs = {'a':1}

I'd like to get f() to print something like the following, but I can't
figure out how.
1
2

I think there's no 'standard' way to do this. but:

import inspect
f(*map(kwargs.get, inspect.getargspec(f)[0])+args)

i don't know if it works because i'm afraid to try it. if it doesn't the
solution is something similar to that.
 
I

inhahe

1
actually, you don't want it to print 3 also? if not, then you would do
f(*map(kwargs.get, inspect.getargspec(f)[0])+args[:1])
import inspect
f(*map(kwargs.get, inspect.getargspec(f)[0])+args)
 
B

bukzor

actually, you don't want it to print 3 also?  if not, then you would do
f(*map(kwargs.get, inspect.getargspec(f)[0])+args[:1])
import inspect
f(*map(kwargs.get, inspect.getargspec(f)[0])+args)

No, that was a typo. Thanks tho.
 
B

bukzor

This question seems easy but I can't figure it out.
Lets say there's a function:
def f(a, *args):
   print a
   for b in args: print b
and elsewhere in your program you have a list and a dict like this:
args = [2, 3]
kwargs = {'a':1}
I'd like to get f() to print something like the following, but I can't
figure out how.
1
2

I think there's no 'standard' way to do this. but:

import inspect
f(*map(kwargs.get, inspect.getargspec(f)[0])+args)

i don't know if it works because i'm afraid to try it.  if it doesn't the
solution is something similar to that.

That does, in fact work. Thanks! I'm a little sad that there's no
builtin way to do it, owell.
... print a
... for b in args: print b
...
import inspect
a = [2,3]
b = {'a':1}
inspect.getargspec(f) (['a'], 'args', None, None)
map(b.get, inspect.getargspec(f)[0]) [1]
map(b.get, inspect.getargspec(f)[0]) + a [1, 2, 3]
f(*map(b.get, inspect.getargspec(f)[0]) + a)
1
2
3
 
I

inhahe

Nick Craig-Wood said:
bukzor said:
That does, in fact work. Thanks! I'm a little sad that there's no
builtin way to do it, owell.
def f(a, *args):
... print a
... for b in args: print b
...
import inspect
a = [2,3]
b = {'a':1}
inspect.getargspec(f) (['a'], 'args', None, None)
map(b.get, inspect.getargspec(f)[0]) [1]
map(b.get, inspect.getargspec(f)[0]) + a [1, 2, 3]
f(*map(b.get, inspect.getargspec(f)[0]) + a)
1
2
3

If I saw that in my code I'd be wanting to get rid of it as soon as
possible!

I'd re-write f() to have all named arguments then the problem becomes
easy and the answer pythonic (simple dictionary manipulation)...

So instead of f(a, *args) have f(a, list_of_args).

The f(*args) syntax is tempting to use for a function which takes a
variable number of arguments, but I usually find myself re-writing it
to take a list because of exactly these sort of problems. In fact I'd
be as bold to say that f(*args) is slightly un-pythonic and you should
avoid as a user interface. It does have its uses when writing
polymorphic code though.


(I second that) the function should definitely be changed if possible. it's
hard to say what the real constraints of the problem are, so I just answered
his question as if that's what he actually needed (assuming the length of
the list and number of named arguments would be variable).

if we assume the constraints are that:
1.he has list, l
2.he has a dictionary, d
3.he wants the function to print the values in the dictionary according to a
specific order of their keys as defined by the function, followed by the
values of the list
then:

def f(d, l):
order_of_keys = ['a']
for key in order_of_keys:
print d[key]
for element in l:
print element

#not tested
 
I

inhahe

if we assume the constraints are that:
1.he has list, l
2.he has a dictionary, d
3.he wants the function to print the values in the dictionary according to
a specific order of their keys as defined by the function, followed by the
values of the list

It's also possible (even likely) that he knows outside of the function what
order he wants the values in, and only used an (incidentally) unordered dict
called kwargs because he thought that's the only way to pass to those
parameters. in which case the function could be left untouched and the he
would call it like this:

args = [1,2,3]
f(*args)
or
f(*[1,2,3])
 
B

bukzor

if we assume the constraints are that:
1.he has list, l
2.he has a dictionary, d
3.he wants the function to print the values in the dictionary according to
a specific order of their keys as defined by the function, followed by the
values of the list

It's also possible (even likely) that he knows outside of the function what
order he wants the values in, and only used an (incidentally) unordered dict
called kwargs because he thought that's the only way to pass to those
parameters.  in which case the function could be left untouched and the he
would call it like this:

args = [1,2,3]
f(*args)
or
f(*[1,2,3])

The actual application is an option-parser wrapper (it inherits from
optparse.OptionParser). I use the option setting to generate a dict of
the entry-point arguments versus the values specified by the
commandline. This works well. I can then do main(**options).

I was trying to add support for an entrypoint signature like
main(a,b,c,*argv) so that you can have a script with a variable number
of arguments. I can then make a script like: ./script.py a b c ...,
which requires quotes right now. (The actual application is a command
wrapper, where I would like to just add the script to the front of a
command without modification.)

It's easy to get the dictionary I had previously (of arguments versus
values) and a list of extraneous values to be passed to argv, but I
realized that main(*argv, **options) wasn't going to work.

I wish this worked:
def main(a,b,*argv): pass
options['argv'] = argv
main(**options)
TypeError: main() got an unexpected keyword argument 'argv'


Thanks for the help,
--Buck
 
B

bukzor

if we assume the constraints are that:
1.he has list, l
2.he has a dictionary, d
3.he wants the function to print the values in the dictionary according to
a specific order of their keys as defined by the function, followed by the
values of the list

It's also possible (even likely) that he knows outside of the function what
order he wants the values in, and only used an (incidentally) unordered dict
called kwargs because he thought that's the only way to pass to those
parameters.  in which case the function could be left untouched and the he
would call it like this:

args = [1,2,3]
f(*args)
or
f(*[1,2,3])

You're actually pretty dead-on here. I preferred the keyword approach
because printing it showed an explicit assignment of arguments, but it
looks like I'll have to use a list if there's an *argv argument, since
there's no way to assign it with keywords, and no (working) way to use
them together.

def f(a, *args): print a, args
args = [2, 3]
kwargs = {'a':1}

f(*args, **kwargs)
TypeError: f() got multiple values for keyword argument 'a'

kwargs['args'] = args
f(**kwargs)
TypeError: f() got an unexpected keyword argument 'args'
 
I

inhahe

"
I wish this worked:
def main(a,b,*argv): pass
options['argv'] = argv
main(**options)
TypeError: main() got an unexpected keyword argument 'argv'
"
-----
I was thinking about that exact same thing actually. Except that I was
thinking you might want it like this, otherwise it could be ambiguous:
def main(a,b,*argv): pass
options['*argv'] = argv
main(**options)

Weird I know, to put the * operator inside the string. I suppose the
necessity of doing that just shows why it wasn't implemented in the first
place. But still, it would be neat...

Also, of course, you could then do
main(*argv=[2,3])

or rather

main(*argv=[3,4],a=1,b=2) #in random order
 
B

bukzor

"
I wish this worked:>>> def main(a,b,*argv): pass
options['argv'] = argv
main(**options)

TypeError: main() got an unexpected keyword argument 'argv'
"
-----
I was thinking about that exact same thing actually. Except that I was
thinking you might want it like this, otherwise it could be ambiguous:
def main(a,b,*argv): pass
options['*argv'] = argv
main(**options)

Weird I know, to put the * operator inside the string. I suppose the
necessity of doing that just shows why it wasn't implemented in the first
place. But still, it would be neat...

Also, of course, you could then do
main(*argv=[2,3])

or rather

main(*argv=[3,4],a=1,b=2) #in random order

Yes I think something like that would be an improvement. I wonder if
anyone would help me write a PEP... It might not be too hard to pass
since it would be compatible with all existing code. I'd be willing to
produce a patch against python2 or py3k.

I don't see that leaving off the * makes it ambiguous, since there can
only be one argument with that name:
def f(args, *args): pass
SyntaxError: duplicate argument 'args' in function definition
 
B

bukzor

"
I wish this worked:>>> def main(a,b,*argv): pass
options['argv'] = argv
main(**options)
TypeError: main() got an unexpected keyword argument 'argv'
"
-----
I was thinking about that exact same thing actually. Except that I was
thinking you might want it like this, otherwise it could be ambiguous:
def main(a,b,*argv): pass
options['*argv'] = argv
main(**options)
Weird I know, to put the * operator inside the string. I suppose the
necessity of doing that just shows why it wasn't implemented in the first
place. But still, it would be neat...
Also, of course, you could then do
main(*argv=[2,3])
or rather
main(*argv=[3,4],a=1,b=2) #in random order

Yes I think something like that would be an improvement. I wonder if
anyone would help me write a PEP... It might not be too hard to pass
since it would be compatible with all existing code. I'd be willing to
produce a patch against python2 or py3k.

I don't see that leaving off the * makes it ambiguous, since there can
only be one argument with that name:
def f(args, *args): pass
SyntaxError: duplicate argument 'args' in function definition

I guess that's a no...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,121
Latest member
LowellMcGu
Top