I have a script I'm working on where I need to accept multiple arguments and then iterate over them to perform actions. I started down the path of defining a function and using *args. So far I have something like below:
def userInput(ItemA, ItemB, *args):
THIS = ItemA
THAT = ItemB
MORE = *args
What I'm trying to do is get the arguments from *args into a list that I can iterate over. I've looked at other questions on StackOverflow as well as on Google but I can't seem to find an answer to what I want to do. Thanks in advance for the help.
To get your precise syntax:
def userInput(ItemA, ItemB, *args):
THIS = ItemA
THAT = ItemB
MORE = args
print THIS,THAT,MORE
userInput('this','that','more1','more2','more3')
You remove the * in front of args in the assignment to MORE. Then MORE becomes a tuple with the variable length contents of args in the signature of userInput
Output:
this that ('more1', 'more2', 'more3')
As others have stated, it is more usual to treat args as an iterable:
def userInput(ItemA, ItemB, *args):
lst=[]
lst.append(ItemA)
lst.append(ItemB)
for arg in args:
lst.append(arg)
print ' '.join(lst)
userInput('this','that','more1','more2','more3')
Output:
this that more1 more2 more3
>>> def foo(x, *args):
... print "x:", x
... for arg in args: # iterating! notice args is not proceeded by an asterisk.
... print arg
...
>>> foo(1, 2, 3, 4, 5)
x: 1
2
3
4
5
edit: See also How to use *args and **kwargs in Python (As referenced by Jeremy D and subhacom).
If you do that :
def test_with_args(farg, *args):
print "formal arg:", farg
for arg in args:
print "other args:", arg
Other information: http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/
MORE = args
Or, directly:
for arg in args:
print "An argument:", arg
If your question is "how do I iterate over args", then the answer is "the same way you iterate over anything": for arg in args: print arg.
Just iterate over args:
def userInput(ItemA, ItemB, *args):
THIS = ItemA
THAT = ItemB
for arg in args:
print arg
Related
In a similar way to using varargs in C or C++:
fn(a, b)
fn(a, b, c, d, ...)
Yes. You can use *args as a non-keyword argument. You will then be able to pass any number of arguments.
def manyArgs(*arg):
print "I was called with", len(arg), "arguments:", arg
>>> manyArgs(1)
I was called with 1 arguments: (1,)
>>> manyArgs(1, 2, 3)
I was called with 3 arguments: (1, 2, 3)
As you can see, Python will unpack the arguments as a single tuple with all the arguments.
For keyword arguments you need to accept those as a separate actual argument, as shown in Skurmedel's answer.
Adding to unwinds post:
You can send multiple key-value args too.
def myfunc(**kwargs):
# kwargs is a dictionary.
for k,v in kwargs.iteritems():
print "%s = %s" % (k, v)
myfunc(abc=123, efh=456)
# abc = 123
# efh = 456
And you can mix the two:
def myfunc2(*args, **kwargs):
for a in args:
print a
for k,v in kwargs.iteritems():
print "%s = %s" % (k, v)
myfunc2(1, 2, 3, banan=123)
# 1
# 2
# 3
# banan = 123
They must be both declared and called in that order, that is the function signature needs to be *args, **kwargs, and called in that order.
If I may, Skurmedel's code is for python 2; to adapt it to python 3, change iteritems to items and add parenthesis to print. That could prevent beginners like me to bump into:
AttributeError: 'dict' object has no attribute 'iteritems' and search elsewhere (e.g. Error “ 'dict' object has no attribute 'iteritems' ” when trying to use NetworkX's write_shp()) why this is happening.
def myfunc(**kwargs):
for k,v in kwargs.items():
print("%s = %s" % (k, v))
myfunc(abc=123, efh=456)
# abc = 123
# efh = 456
and:
def myfunc2(*args, **kwargs):
for a in args:
print(a)
for k,v in kwargs.items():
print("%s = %s" % (k, v))
myfunc2(1, 2, 3, banan=123)
# 1
# 2
# 3
# banan = 123
Adding to the other excellent posts.
Sometimes you don't want to specify the number of arguments and want to use keys for them (the compiler will complain if one argument passed in a dictionary is not used in the method).
def manyArgs1(args):
print args.a, args.b #note args.c is not used here
def manyArgs2(args):
print args.c #note args.b and .c are not used here
class Args: pass
args = Args()
args.a = 1
args.b = 2
args.c = 3
manyArgs1(args) #outputs 1 2
manyArgs2(args) #outputs 3
Then you can do things like
myfuns = [manyArgs1, manyArgs2]
for fun in myfuns:
fun(args)
def f(dic):
if 'a' in dic:
print dic['a'],
pass
else: print 'None',
if 'b' in dic:
print dic['b'],
pass
else: print 'None',
if 'c' in dic:
print dic['c'],
pass
else: print 'None',
print
pass
f({})
f({'a':20,
'c':30})
f({'a':20,
'c':30,
'b':'red'})
____________
the above code will output
None None None
20 None 30
20 red 30
This is as good as passing variable arguments by means of a dictionary
Another way to go about it, besides the nice answers already mentioned, depends upon the fact that you can pass optional named arguments by position. For example,
def f(x,y=None):
print(x)
if y is not None:
print(y)
Yields
In [11]: f(1,2)
1
2
In [12]: f(1)
1
I am searching how I could use optional arguments in python.
I have read this question but it is not clear to me.
Lets say I have a function f that can take 1 or more arguments to understand time series. Am i obliged to specify the number of arguments and set default values for each argument?
What I aim to do is being able to write a function this way:
simple function:
def f(x,y):
return x + y
#f(1,2) returns 3
What i want is also f(1,2,3) to return me 6 and f(7) returning me 7
Is it possible to write it without setting a predefined number of mandatory/optional parameters?
Is it possible to write it without having to set default values to 0 ?
How to write this function?
Its a simple example with numbers but the function i need to write is comparing a set of successive objects. After comparison is done, the data set will feed a neural network.
Thanks for reading.
EDIT:
Objects I am feeding my function with are tuples like this (float,float,float,bool,string)
You can put *args in your function and then take arbitrary (non-keyword) arguments. *args is a tuple, so you can iterate over it like any Python tuple/list/iterable. IE:
def f(*args):
theSum = 0
for arg in args:
theSum += arg
return theSum
print f(1,2,3,4)
def f(*args):
"""
>>> f(1, 2)
3
>>> f(7)
7
>>> f(1, 2, 3)
6
>>> f(1, 2, 3, 4, 5, 6)
21
"""
return sum(args)
If you need to do something more complicated than sum you could just iterate over args like this:
def f(*args):
r = 0
for arg in args:
r += arg
return r
See this question for more information on *args and **kwargs
Also see this sections on the Python tutorial: Arbitray Argument List
You can use the follow syntax:
def f(*args):
return sum(args)
The * before args tells it to "swallow up" all arguments, makng args a tuple. You can also mix this form with standard arguments, as long as the *args goes last. For example:
def g(a,b,*args):
return a * b * sum(args)
The first example uses the built-in sum function to total up the arguments. sum takes a sequence as adds it up for you:
>>> sum([1,3,5])
9
>>> sum(range(100))
4950
The args name is not mandatory but is used by convention so best to stick with it. There is also **kwargs for undefined keyword arguments.
For a decorator I am writing I would like to manipulate a specific named parameter of a function. Consider the following decorator:
def square_param(param):
def func_decorator(func):
def func_caller(*args,**kwargs):
kwargs[param] = kwargs[param] * kwargs[param]
return func(*args,**kwargs)
return func_caller
return func_decorator
Applied on the next function:
#square_param('dividend')
def quotient(divisor=1,dividend=0):
return dividend/divisor
This will work if dividend is called as a keyword argument e.g.:
>>> quotient(dividend=2)
4
However, when given as a positional argument this will fail.
>>> quotient(3,4)
TypeError: quotient() got multiple values for keyword argument 'dividend'
With Python 3 I could solve this by forcing the parameter to be always given as a keyword:
#square_param('dividend')
def quotient(divisor=1,*,dividend=0):
return dividend/divisor
but I would like to support Python 2 and also I would like to put as little restrictions on the function.
Is there a way that I can fix this behaviour in my decorator?
Firstly, your square_param decorator doesn't work because it doesn't return the functions. It needs to be:
def square_param(param):
def func_decorator(func):
def func_caller(*args,**kwargs):
kwargs[param] = kwargs[param] * kwargs[param]
return func(*args,**kwargs)
return func_caller
return func_decorator
Now I took #Dirk's advice and looked into the inspect module. You can do it by checking first if the parameter is one of the function's positional arguments, and second if that positional argument has been specified, and then modifying that argument position. You also need to make sure you only modify kwargs if the parameter was supplied as a keyword argument.
import inspect
def square_param(param):
def func_decorator(func):
def func_caller(*args,**kwargs):
funparams = inspect.getargspec(func).args
if param in funparams:
i = funparams.index(param)
if len(args) > i:
args = list(args) # Make mutable
args[i] = args[i] * args[i]
if param in kwargs:
kwargs[param] = kwargs[param] * kwargs[param]
return func(*args,**kwargs)
return func_caller
return func_decorator
even without using Inspect we can get function params
>>> func = lambda x, y, args: (x, y, {})
>>> func.func_code.co_argcount
3
>>> func.func_code.co_varnames
('x', 'y', 'args')
This may only be tangentially related, but I found it useful to solve a similar problem. I wanted to meld *args and **kwargs into a single dictionary so that my following code could process without regard to how the args came in, and I didn't want to mutate the existing kwargs variable, otherwise I just would have use kwargs.update().
all_args = {**kwargs, **{k: v for k, v in zip(list(inspect.signature(func).parameters), args)}}
# optionally delete `self`
del (all_args['self'])
Update: While this works, this answer has a better technique. In part:
bound_args = inspect.signature(f).bind(*args, **kwargs)
bound_args.apply_defaults()
My code:
import threading
def hello(arg, kargs):
print arg
t = threading.Timer(2, hello, "bb")
t.start()
while 1:
pass
The print out put is just:
b
How can I pass a argument to the callback? What does the kargs mean?
Timer takes an array of arguments and a dict of keyword arguments, so you need to pass an array:
import threading
def hello(arg):
print arg
t = threading.Timer(2, hello, ["bb"])
t.start()
while 1:
pass
You're seeing "b" because you're not giving it an array, so it treats "bb" an an iterable; it's essentially as if you gave it ["b", "b"].
kwargs is for keyword arguments, eg:
t = threading.Timer(2, hello, ["bb"], {arg: 1})
See http://docs.python.org/release/1.5.1p1/tut/keywordArgs.html for information about keyword arguments.
The third argument to Timer is a sequence. Since you pass "bb" as that sequence, hello gets the elements of that sequence ("b" and "b") as separate arguments (arg and kargs). Put "bb" in a list and hello will get the string as the first argument.
t = threading.Timer(2, hello, ["bb"])
As for hello's parameters, you probably mean:
def hello(*args, **kwargs):
The meaning of **kwargs is covered in the queston "What does *args and **kwargs mean?"
In a similar way to using varargs in C or C++:
fn(a, b)
fn(a, b, c, d, ...)
Yes. You can use *args as a non-keyword argument. You will then be able to pass any number of arguments.
def manyArgs(*arg):
print "I was called with", len(arg), "arguments:", arg
>>> manyArgs(1)
I was called with 1 arguments: (1,)
>>> manyArgs(1, 2, 3)
I was called with 3 arguments: (1, 2, 3)
As you can see, Python will unpack the arguments as a single tuple with all the arguments.
For keyword arguments you need to accept those as a separate actual argument, as shown in Skurmedel's answer.
Adding to unwinds post:
You can send multiple key-value args too.
def myfunc(**kwargs):
# kwargs is a dictionary.
for k,v in kwargs.iteritems():
print "%s = %s" % (k, v)
myfunc(abc=123, efh=456)
# abc = 123
# efh = 456
And you can mix the two:
def myfunc2(*args, **kwargs):
for a in args:
print a
for k,v in kwargs.iteritems():
print "%s = %s" % (k, v)
myfunc2(1, 2, 3, banan=123)
# 1
# 2
# 3
# banan = 123
They must be both declared and called in that order, that is the function signature needs to be *args, **kwargs, and called in that order.
If I may, Skurmedel's code is for python 2; to adapt it to python 3, change iteritems to items and add parenthesis to print. That could prevent beginners like me to bump into:
AttributeError: 'dict' object has no attribute 'iteritems' and search elsewhere (e.g. Error “ 'dict' object has no attribute 'iteritems' ” when trying to use NetworkX's write_shp()) why this is happening.
def myfunc(**kwargs):
for k,v in kwargs.items():
print("%s = %s" % (k, v))
myfunc(abc=123, efh=456)
# abc = 123
# efh = 456
and:
def myfunc2(*args, **kwargs):
for a in args:
print(a)
for k,v in kwargs.items():
print("%s = %s" % (k, v))
myfunc2(1, 2, 3, banan=123)
# 1
# 2
# 3
# banan = 123
Adding to the other excellent posts.
Sometimes you don't want to specify the number of arguments and want to use keys for them (the compiler will complain if one argument passed in a dictionary is not used in the method).
def manyArgs1(args):
print args.a, args.b #note args.c is not used here
def manyArgs2(args):
print args.c #note args.b and .c are not used here
class Args: pass
args = Args()
args.a = 1
args.b = 2
args.c = 3
manyArgs1(args) #outputs 1 2
manyArgs2(args) #outputs 3
Then you can do things like
myfuns = [manyArgs1, manyArgs2]
for fun in myfuns:
fun(args)
def f(dic):
if 'a' in dic:
print dic['a'],
pass
else: print 'None',
if 'b' in dic:
print dic['b'],
pass
else: print 'None',
if 'c' in dic:
print dic['c'],
pass
else: print 'None',
print
pass
f({})
f({'a':20,
'c':30})
f({'a':20,
'c':30,
'b':'red'})
____________
the above code will output
None None None
20 None 30
20 red 30
This is as good as passing variable arguments by means of a dictionary
Another way to go about it, besides the nice answers already mentioned, depends upon the fact that you can pass optional named arguments by position. For example,
def f(x,y=None):
print(x)
if y is not None:
print(y)
Yields
In [11]: f(1,2)
1
2
In [12]: f(1)
1