Given a list and a dictionary, I want to calculate the sum of its elements. Here's how I tried to do it
def sum(*arg , **argv):
S=0
for i , j in arg, list(argv.values()):
S = i j
return S
li=[1,2]
dic={'a':1 , 'b':2}
print(sum(li,dic))
But I get this error : TypeError: can only concatenate list (not "dict") to list what's the problem ?
Remarque: I'm writing this code just because I'm trying to understand the * and ** in python
CodePudding user response:
If you print out the values of arg
and argv
in the function, you will see that the value of arg
is ([1, 2], {'a': 1, 'b': 2})
while the value of argv
is an empty dict {}
, which is most likely not what you intended. Now there are multiple problems with that you are trying to do.
First, sum
is already a builtin function in Python, so it is bad practice to redefine it. Though this is not the issue that is causing your code to error.
Second, let's take a look at the for
loop more closely:
for i , j in arg, list(argv.values()):
S = i j
Note that arg
is ([1, 2], {'a': 1, 'b': 2})
and argv
is {}
, which means list(argv.values())
is an empty list []
(at least for your example function call). Instead of calling S = i j
, we will print out i
and j
to see what their values are:
for i , j in arg, list(argv.values()):
print(i)
print(j)
If you run this, it will print out [1, 2]
(which is i
) and {'a': 1, 'b': 2}
(which is j
), and then end in an error:
ValueError: not enough values to unpack (expected 2, got 0)
Now we see what the problem is. In the first pass through the loop, arg
is unpacked, which causes i
and j
to be assigned the values of the first and second elements of arg
respectively. So when S = i j
is ran, it will run into an error when trying to compute i j
, because i
is a list while j
is a dict.
And the reason why we got the ValueError
when we took out S = i j
was because on the second iteration of the loop, it attempts to unpack list(argv.values())
, but it is an empty list, so it can't be unpacked into two values.
Third, one last reason why your code won't work is because you initialized S
to have a value of 0
, which is an integer. Even if, miraculously, i
and j
were both lists, the code will still fail due to not being able to add an integer with a list:
TypeError: unsupported operand type(s) for =: 'int' and 'list'
With all that in mind, here are a few fixes I recommend:
First, change your function name to something else, other than sum
, so that it doesn't conflict with the builtin sum
function. I will change it to arg_sum
for now.
Second, to make sure arg
and argv
have the intended values, you will also need to include the unpack operators in the function call itself. Instead of arg_sum(li, dic)
, trying doing arg_sum(*li, **dic)
. You will see that arg
has the value [1, 2]
and that argv
has the value {'a': 1, 'b': 2}
, which is most likely what you intended.
Third, for i, j in arg, list(argv.values())
will unpack in unintended ways. With the fixes I've already mentioned above, i
and j
will have the values 1
and 2
respectively on the first iteration (arg
gets unpacked into two values), and they will also be 1
and 2
respectively on the second iteration (list(argv.values())
gets unpacked into two values). Coincidentally, this will actually give you the correct result, but this is only because li
and dic
have only two values; it will throw a ValueError
for any other length.
Instead, what you want is the zip
function, which will pair the two lists element-wise. So i
and j
will take on the values of the first element of the two lists in the first iteration, the second element in the second iteration, and so on.
Taking all these fixes into account, here is the final, revised code:
def arg_sum(*arg, **argv):
S = 0
for i, j in zip(arg, list(argv.values())):
S = i j
return S
li = [1, 2]
dic = {'a': 1, 'b': 2}
print(arg_sum(*li, **dic))