‘Got multiple values for argument’ error with keyword arguments in Python classes
This is a quick post to brief describe a problem I ran into the other day when trying to debug someone’s code – the answer may be entirely obvious to you, but it took me a while to work out, so I thought I’d document it here.
The problem that I was called over to help with was a line of code like this:
t.do_something(a=5, b=10)
where t
was an instance of a class. Now, this wasn’t the way that I usually write code – I tend to only use keyword arguments after I’ve already used positional arguments – but it reminded me that in Python the following calls to the function f(a, b)
are equivalent:
f(1, 2)
f(1, b=2)
f(a=1, b=2)
Anyway, going back to the original code: it gave the following error:
do_something() got multiple values for argument 'a'
which I thought was very strange, as there was definitely only one value of a
given in the call to that method.
If you consider yourself to be a reasonably advanced Python programmer than you might want to stop here and see if you can work out what the problem is. Any ideas?
When you’ve had a bit of a think, continue below…
I had a look at the definition of t.do_something()
, and it looked like this:
class Test:
def do_something(a, b):
# Do something here!
print('a = %s' % a)
print('b = %s' % b)
You may have noticed the problem now – although at first glance I couldn’t see anything wrong… There was definitely only one parameter called a
, and it definitely wasn’t being passed twice…so what was going on?!
As you’ve probably noticed by now…this method was missing the self
parameter – and should have been defined as do_something(self, a, b)
. Changing it to that made it work fine, but it’s worth thinking about exactly why we were getting that specific error.
Firstly, let’s have a look at a more ‘standard’ error that you might get when you forget to add self
as the first argument for an instance method. We can see this by just calling the method without using keyword arguments (that is, t.do_something(1, 2)
), which gives:
TypeError: test_noself() takes 2 positional arguments but 3 were given
Now, once you’ve been programming Python for a while you’ll be fairly familiar with this error from when you’ve forgotten to put self
as the first parameter for an instance method. The reason this specific error is produced is that Python will always pass instance methods the value of self
as well as the arguments you’ve given the method. So, when you run the code:
t.do_something(1, 2)
Python will change this ‘behind the scenes’, and actually run:
t.do_something(t, 1, 2)
and as do_something
is only defined to take two arguments, you’ll get an error. Of course, if your function had been able to take three arguments (for example, if there was an optional third argument), then you would find that t
(which is the value of self
in this case) was being passed as the first argument (a
), 1
as the value of the second argument (b
) and 2
as the value of the third argument (which could have been called c
). This is a good point to remind you that the first argument of methods is only called self
by convention – and that Python itself doesn’t care what you call it (although you should always call it self
!)
From this, you should be able to work out why you’re getting an error about getting multiple values for the argument a
… What’s happening is that Python is passing self
to the method, as the first argument (which we have called a
), and is then passing the two other arguments that we specified as keyword arguments. So, the ‘behind the scenes’ code calling the function is:
t.do_something(t, a=1, b=2)
But, the first argument is called a
, so this is basically equivalent to writing:
t.do_something(a=t, a=1, b=2)
which is obviously ambiguous – and so Python throws an error.
Interestingly, it is quite difficult to get into a situation in which Python throws this particular error – if you try to run the code above you get a different error:
SyntaxError: keyword argument repeated
as Python has realised that there is a problem from the syntax, before it even tries to run it. You can manage it by using dictionary unpacking:
def f(a, b):
pass
d = {'a':1, 'b':2}
f(1, **d)
Here we are defining a function that takes two arguments, and then calling it with a single positional argument for a
, and then using the **
method of dictionary unpacking to take the dictionary d
and convert each key-value pair to a keyword argument and value combination.
So, congratulations if you’d have solved this problem far quicker than me – but I hope it has made you think a bit more about how Python handles positional and keyword arguments. A few points to remember:
- Always remember to use
self
as the first argument of your methods! (This would have stopped this problem ever happening!) - But remember that the name
self
is just a convention, and Python will pass the instance of your class to your first argument regardless what it is called, which can cause weird problems. - All positional arguments can be passed as keyword arguments, and vice-versa – they are entirely interchangeable – which, again, can cause problems if this isn’t what you intended.
If you found this post useful, please consider buying me a coffee.
This post originally appeared on Robin's Blog.
Categorised as: Programming, Python
You have an error in your list of possible function calls.
a(a=1, 2) don’t work!
In [5]: a(a=1, 2)
File “”, line 1
a(a=1, 2)
SyntaxError: non-keyword arg after keyword arg
Thanks – good catch. I’ve fixed it now.
Thank you! I am a Python beginner and have seen the “self” references, but never understood them until I started running into this error. Thanks for the explanation!
Thanks, this saved me a lot of time. I wasn’t sure where to look as the ‘self’ argument was missing.
Thank you so much. I finally understand why my function didn’t work out after searching the problem from all the other websites.
I was completely perplexed by this error message. It prompted me to make a bunch of enhancements to my code but then I wound up getting the same message on a different parameter (I had changed what was passed in first). That’s when I decided okay, wait, maybe I should google this error message, and that’s when I landed on your blog entry from 3 years ago. Thank you for posting this. I am relatively experienced at programming but relatively inexperienced at Python. So this was exactly what I needed.
This is a great explanation. I am rather experienced in Python, but this one had me stumped. I am using Django as a web framework with python. I created a method to build a PDF file and serve it as a download. The method took one positional argument and two keyword arguments. When I opened the URL it gave this error about multiple versions of the first parameter. The method is not a class method, but Django passes in the HTTP Request object to any function it calls. So, for the same reason, it was passing the extra request parameter before the others and generating the same error. Thank you, as soon as I saw self, I knew where I made the mistake and had to add request as the first argument.
Thank you! Saved me a lot of time scratching my head.
Thank you so much!
Thanks so much for the comment,
“Always remember to use self as the first argument of your methods! (This would have stopped this problem ever happening!)”
This post just helped me debug a very 9 am problem. I was calling the pandas melt function as melt(df, id_vars=foo, value_vars=bar), and was hitting “TypeError: melt() got multiple values for argument ‘id_vars'”. I stared for ages at the definitions of id_vars and value_vars and nothing seemed to contradict. I eventually found this post, and as soon as I hit the sentence “this method was missing the self parameter” I realized what the issue must be … turns out in my pre-coffee haze I had accidentally written df.melt(df, id_vars=foo, value_vars=bar), passing the dataframe df to the melt function twice instead of just invoking the pandas library!
Thank you very much, though the method was taking kwargs, the mistake i did was, i specifically declared value for 1 parameter and missed declaring value for another parameter