I inspected the method using inspect module. The getcallargs() method from the inspect module, gives you the mapping of the values passed, to the arguments of the method.
def total(name, *args):
if args:
print("%s has total money of Rs %d/- " %(name, sum(args)))
else:
print("%s's piggy bank has no money" %name)
#total(name="Adi", *(1, 2, 10) )
# equivalent call using inspect module
import inspect
print inspect.getcallargs(total, 'Adi', *(1, 2, 10) )
So, when a call to a method is made, all the arguments passed goes in either of the following two forms
*positional :- a tuple of all positional arguments (without keyword) and arguments passed within *() or the extra non-keyworded arguments
**named :- a dictionary of all the arguments passed in form of param=value, and extra keyworded arguments
So in my call total(name="Adi", *(1, 2, 10) ), 'positional' tuple has value (1,2,10) and 'named' dictionary has value {'name':'Adi'}
Then it does the assignment of the values to the arguments. During assignment, compulsory arguments are assigned first. Python checks the list of compulsory arguments (here ['name']) and assigns the value from the 'positional' tuple sequentially. Any values from the tuple left unassigned, are assumed to be extra parameters (non-keyworded arguments).
Thus the parameter 'name' gets the value 1 assigned to it. And the remaining values (i.e. 2 and 10) are assumed as extra parameters.
Next, it checks, if the method has non-keyworded arguments in it's signature. If so, then the remaining values from 'positional' tuple are assigned to the same. So here 'args' gets assigned to the remaining values i.e. tuple of (2,10).
But, if there is no non-keyword arguments in method signature, and 'positional' tuple has some values still un-assigned, then python throws the error "method takes exactly X arguments Y given" Y being greater than X.
Once all the values from 'positional' are assigned, the assignment of values from the 'named' dictionary gets assigned. In this case, interpreter first checks, if any of the compulsory arguments are present in the dictionary, i.e. if values to any of the compulsory arguments are passed in form of <param=value> in the method call? If yes, then next it checks if any value has been assigned to those compulsory arguments during the assignment of values from 'positional' tuple?
If so, then it finds two values for the same argument (one from 'positional' and the other from 'named'), and hence throws "got multiple values for keyword argument" error. Otherwise, it assigns the value to the argument from the 'named' dictionary.
In that manner, in the above case, argument 'name' is compulsory argument and is present in 'named' dictionary as I have passed name="Adi" in the method call. During assignment of positional values (values from 'positional' tuple), variable 'name' has got the value 1 assigned to it. And from the dictionary of named arguments (values from 'named' dictionary), it has go the value 'Adi', which makes 2 values being assigned to the variable 'name'. Hence we are getting the error :-
total() got multiple values for keyword argument 'name'
Therefore, while passing non-keyworded extra arguments, one needs to just pass the value for compulsory arguments, which will make it positional. But, not in form of <param=val>, which will make it named and will get two values for the parameter hence will throw the error. Therefore it is not recommended to pass the compulsory arguments in form of <param = value >.
So the correct ways to call the method are :-
total('Adi', *(1,2,10))
or
total('Adi', 1, 2, 10)
total(name-"Adi", 1, 2, 10 )is not valid because of the-symbol. Are you sure you're showing us the real code? - Peter Wood