arbitrary number of arguments in a python function - python

I'd like to learn how to pass an arbitrary number of args in a python function, so I wrote a simple sum function in a recursive way as follows:
def mySum(*args):
if len(args) == 1:
return args[0]
else:
return args[-1] + mySum(args[:-1])
but when I tested mySum(3, 4), I got this error:
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
Does anyone have an idea about this and gimme some clue to correct it?

This line:
return args[-1] + mySum(args[:-1])
args[:-1] returns a slice of the arguments tuple. I assume your goal is to recursively call your function using that slice of the arguments. Unfortunately, your current code simply calls your function using a single object - the slice itself.
What you want to do instead is to call with those args unrolled.
return args[-1] + mySum(*args[:-1])
^---- note the asterisk
This technique is called "unpacking argument lists," and the asterisk is sometimes (informally) called the "splat" operator.

If you don't want to do it recursively:
def mySum(*args):
sum = 0
for i in args:
sum = sum + i
return sum

args[:-1] is a tuple, so the nested call is actually mySum((4,)), and the nested return of args[0] returns a tuple. So you end up with the last line being reduced to return 3 + (4,). To fix this you need to expand the tuple when calling mySum by changing the last line to return args[-1] + mySum(*args[:-1]).

In your code, args[:-1] is a tuple, so mySum(args[:-1]) is being called with the args being a tuple containing another tuple as the first argument. You want to call the function mySum with args[:-1] expanded to the arguments however, which you can do with
mySum(*args[:-1])

The arbitrary arguments are passed as tuple (with one asterisk*) to the function, (you can change it to a list as shown in the code) and calculate the sum of its elements, by coding yourself using a for loop; if don't want to use the sum() method of python.
def summing(*arg):
li = list(*arg)
x = 0
for i in range((len(li)-1)):
x = li[i]+x
return x
#creating a list and pass it as arbitrary argument to the function
#to colculate the sum of it's elements
li = [4, 5 ,3, 24, 6, 67, 1]
print summing(li)

Option1:
def mySum(*args):
return sum(args)
mySum(1,2,3) # 6
mySum(1,2) # 3
Option 2:
mySum2 = lambda *args: sum(args)
mySum2(1,2,3) # 6
mySum2(1,2) # 3

Related

What is the point of using *args when a list of arguments can be used?

Would passing in a list or dictionary of variables be more concise than passing in *args in Python methods?
For example,
def function(a, *argv):
print('First variable:', a)
for k in argv:
print('Additional variable:',k)
is the same as
def function(a, list):
print('First variable:', a)
for k in list:
print('Additional variable:',k)
except a list is passed in the second argument. What I think using *args would often do is to cause additional bugs in the program because the argument length only needs to be longer than the mandatory argument length. Would any please explain situations where *args would be really helpful? Thanks
The first function accepts:
function('hello', 'I', 'am', 'a', 'function')
The second one won't. For the second you'd need:
function('hello', ['I', 'am', 'a', 'function'])
In principle, the first one is used when your function can have an arbitrary number of parameters (think: print), while the second one specifies that there's always a second parameter, which is an iterable (not necessarily a list, despite the name)
Passing *args is useful when you have to extract only some (or none) arguments in first level function and then pass others to other inner function without knowing about the details. e.g.
def inner_func(x, y, z, *args, **kwargs):
# do something with x, y, and z
return output
def outer_func(a, b, *args, **kwargs):
# do something with a and b
# pass the rest arguments to inner function without caring about details
output = inner_func(*args, **kwargs)
# do something with output
return
That is a fair ask as to why *args (or **kwargs) is essentially required when a list (or dict) could do the same task. The key reason to that is when a ** caller of a function does not know the number of arguments beforehand**. I'll try to explain this with reference to the particular scenario you have shared.
Lets suppose that we have the below function which finds the sum of all integers passed in. (I'm giving up sum builtin function for demonstration purpose, please bear with me :) )
def do_add(*int_args):
total = 0
for num in int_args:
total += num
return total
And you want to call this for an unknown number of arguments with an unknown number of times.
If in case you need to send a list argument, the do_add function might look like below:
def do_add(int_list):
total = 0
for num in int_list:
total += 0
return total
l1 = [1, 2, 3, 4, ... n] # memory loaded with n int objects
do_add(l1)
l2 = [10, 20, ... n]
do_add(l2)
Firstly, you are loading the memory with an additional list object created just for the sake of function call. Secondly, if you have to add some more items to the list we may need to call another list method such as append or extend.
But if you follow the *args approach, you can avoid creating an extra list and focus only on the function call. If you need to add more arguments you can just add another argument separated by a comma rather than calling append or extend methods.
Assume that you want to call this function n times with 1000 arguments. It will result in n * 1000 new list objects to be created every time. But with the variable arguments approach, you can just call it directly.
do_add(1, 2, 3) # call 1
do_add(10.0, 20.0, 30.0) # call 2
...
do_add(x, y, z, ..., m) # call n

How do I convert this list sort function from Python 2 to Python 3

When I try to port it it errors out asking for key2
Python 2:
def SortItems(self,sorter=cmp):
items = list(self.itemDataMap.keys())
items.sort(sorter)
self.itemIndexMap = items
self.Refresh()
Python 3:
try:
cmp
except NameError:
def cmp(x, y):
if x < y:
return -1
elif x > y:
return 1
else:
return 0
def SortItems(self,sorter=cmp):
items = list(self.itemDataMap.keys())
items.sort(key=sorter)
self.itemIndexMap = items
self.Refresh()
Getting the error:
items.sort(key=sorter)
TypeError: __ColumnSorter() missing 1 required positional argument: 'key2'
It looks like lambda function needs second argument
Any idea how to make it work?
Also tried functools.cmp_to_key:
def SortItems(self):
import locale
items = list(self.itemDataMap.keys())
items= sorted(items, key=cmp_to_key(locale.strcoll))
self.itemIndexMap = items
self.Refresh()
Getting error:
items= sorted(items, key=cmp_to_key(locale.strcoll))
TypeError: strcoll() argument 1 must be str, not int
Probably because I'm sorting integers not strings
How do I make it work for int?
cmp and key are fundamentally different. However there is a conversion function you can use: functools.cmp_to_key().
From the docs Python3 list.sort():
sort() accepts two arguments that can only be passed by keyword
(keyword-only arguments)
key specifies a function of one argument that is used to extract a
comparison key from each list element (for example, key=str.lower).
That is, the key callable only takes a single argument in py3. So in this case doing
items.sort(int), or equivalently items.sort(lambda x: x)
will sort a list of int in ascending order.
In general cmp should return the comparison property of the each element
of the list.
def cmp(x):
# code to compute comparison property or value of x
# eg. return x % 5
Additionally, you can convert the python2 cmp function:
The functools.cmp_to_key() utility is available to convert a 2.x style
cmp function to a key function.
https://docs.python.org/3/library/stdtypes.html#list.sort

How to return a single value instead of a tuple?

I realise that in the below functions f returns a tuple, and g returns a list.
def f():
return 1,2
def g():
return [1,2]
a,b=f()
c,d=g()
I have written a function which can handle any number of arguments.
def fun(*args):
return args
These arguments are entered like the f function above because they are the return value from a previous function.
output = fun(other_func())
When more that one value is return from fun the individual values can be retrieved by stating something like this...
output1, output2 = fun(other_func())
However, when one argument is used the output is something like below...
output = fun(other_func())
(1,)
Is there a way when there is only one value to have it return a single element instead of a tuple but still have the functionality of being able to return more than one value?
If you know the function is always going to return a one-element tuple, you can use a one-element tuple assignment:
output, = fun(other_func())
or simply index:
output = fun(other_func())[0]
But in this case, a simple Don't do that, don't return a tuple might also apply:
output = other_func()
As long as *args is a tuple, returning args will therefore return a tuple, even if there is only one parameter.
You should probably do something like:
def fun(*args):
if len(args) == 1:
args, = args
return args
This might be what you are looking for. With this method you must have at least one argument, but you will catch the other arguments in other if you have more.
def funct(*args):
return args
# end funct
if __name__ == '__main__':
foo, *other = funct(1, 2, 3, 4)
print(foo)

sqrt() argument after * must be a sequence

First of all, I'm super new to python and I actually search for my problem but the examples were to heavy to understand.
Here is my homework; I need a function which takes two functions as an argument and returns if the results of the two functions are same or not? Basically, it will give either TRUE of FALSE.
For that I wrote:
def f(x,y,z):
k=x(*z)
l=y(*z)
return k == l
The previos code I wrote for single function was working but when I modified it for two function as above, it gives an error as following :
import math
>>> f(math.sqrt,math.cos,5)
Traceback (most recent call last):
File "<pyshell#56>", line 1, in <module>
f(math.sqrt,math.cos,5)
File "D:/Users/karabulut-ug/Desktop/yalanmakinesi.py", line 2, in f
k=x(*z)
TypeError: sqrt() argument after * must be a sequence
>>>
I could not figured it out since the error giving function is normally does not take a sequence. So I dont think it makes a sense :) Any help is appreciated.. Thanks :)
z is just a single number, but the * argument expansion syntax requires that you pass in a sequence (like a list, tuple or str, for example).
Either remove the * (and make your function work for just single arguments), or use *z in the function signature to make z a tuple of 0 or more captured arguments:
def f(x, y, z):
k = x(z)
l = y(z)
return k == l
or
def f(x, y, *z):
k = x(*z)
l = y(*z)
return k == l
The latter now works for functions with more than one argument too:
f(math.pow, math.log, 10, 10)
If you added a **kw argument to the signature, then keyword arguments could be handled too:
def f(x, y, *args, **kwargs):
k = x(*args, **kwargs)
l = y(*args, **kwargs)
return k == l
Here I renamed z to args to better reflect its purpose.
The syntax *z invokes argument unpacking on z. When z is just an integer, there is no iterator behavior defined, and so you see this error. Try:
>>> f(math.sqrt, math.cos, [5])
You need to remove the *. Its for unpacking. So:
def f(x,y,z):
k=x(z)
l=y(z)
return k == l
You use the * operator when you want to pass in an iterable object, like a list or tuple as something thats split up. So, for example:
a = [1,2,3,4,5]
So, for an arbitrary function, f:
f(*a) = f(1,2,3,4,5)

optional arguments function

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.

Categories