[Python-Dev] Reasons behind misleading TypeError message when passing the wrong number of arguments to a method

Steven D'Aprano steve at pearwood.info
Fri May 21 05:54:56 CEST 2010


On Fri, 21 May 2010 10:53:16 am Greg Ewing wrote:
> Ben Finney wrote:
> >>Something like "1 argument in addition to 'self'" would be
> >> reasonably clear and would cover both situations.
> >
> > Except that there's nothing special to the syntax or parser about
> > the name ‘self’.
>
> That's true, but the use of the word 'self' here isn't meant
> to refer to the name of a parameter. The message is aimed at
> the caller of the function, who doesn't necessarily know or
> care what the parameter is actually called.

And who doesn't necessarily know that:

> The important thing is that it represents the object the method
> is being called for, and 'self' is the traditional term used
> when talking about that. 

Referring to self in any such error message is going to confuse newbies, 
and occasional Python programmers who use it without learning about 
object oriented code. E.g. sys admins who use Python as a scripting 
language without ever writing their own classes.

Error messages should be aimed at the least capable users (within 
reason!) rather than the most capable, since the most capable are:

(1) less likely to make the error in the first place;
(2) more able to interpret the error message correctly;
(3) more likely to understand if the error relates to something "under 
the hood"; and
(4) better able to debug the error even in the absence of a useful error 
message.

Given this, it is better for the error message to relate to what 
the "average" user most likely has in his source code (i.e. a bound 
method) rather than what the better-than-average user occasionally uses 
(an unbound method).


> I can't think of anything that would 
> be more accurate without being excessively verbose or pedantic.


In Python 3.1, strings seem to behave more sensibly IMO:

>>> 'abc'.find()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: find() takes at least 1 argument (0 given)

That looks right to me. I'm calling a *method* with zero arguments 
inside the parentheses when I need to call it with one.

If I call it as an unbound method (yes, I know it's actually a function) 
I get something more confusing:

>>> str.find('abc')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: find() takes at least 1 argument (0 given)

While calling unbound methods isn't exactly rare, it is a more advanced 
thing to do than calling bound methods. If we're stuck with confusing 
messages, better to have it occur for the users who are more likely to 
be able to interpret it, that is, those advanced enough to use unbound 
methods rather than "ordinary" users.

Note that str avoids the "-1 argument" trap:

>>> str.find()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: descriptor 'find' of 'str' object needs an argument



Classes behave in the opposite manner: the error message is technically 
correct for the less common, more advanced case, but misleading for the 
more common, simple case:


>>> class C:
...     def method(self, x):
...             return x+1
...
>>> C.method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 2 positional arguments (0 given)

This is exactly right, since C.method is a function object which takes 
two arguments, self and x. Because this is a function, we can't change 
this (not that we want to!) without impacting the errors you get when 
calling functions not associated with a class.


>>> C().method()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: method() takes exactly 2 positional arguments (1 given)

This is misleading, since C().method is a bound method which takes one 
argument, x, not two. I find myself wishing that Python distinguished 
between ArgumentError and other TypeErrors, so that the method wrapper 
of bound methods could simply catch ArgumentError and subtract 1 from 
each argument count.



-- 
Steven D'Aprano


More information about the Python-Dev mailing list