[Python-Dev] Re: [Python-checkins] CVS: python/dist/src/Include rangeobject.h,2.16,2.17
Tim Peters
tim.one@home.com
Sun, 8 Jul 2001 19:44:22 -0400
[Greg Ward]
> However, I think that AttributeError is pretty aptly used for the most
> part, and I don't see a great benefit in changing an incorrect
> "thing.property" to raise TypeError.
"The problem" comes up again and again, and in every release (this isn't
something new!), in the specific context of instance objects. Like what do
you do for
class C:
pass
c = C()
print len(c)
? Instance objects have *every* interesting tp_xxx slot filled in "just in
case", so the len() implementation code that first checks for the existence
of tp_as_sequence or tp_as_mapping says "NO!" to len(3) but "YES!" to
len(c). The result is that len(3) produces
TypeError: len() of unsized object
but len(c) eventually gets around to raising the superficially different
AttributeError: C instance has no attribute '__len__'
instead. So in cases like this TypeError really means "and it's damned
obvious", while AttributeError means "but it might have been otherwise had
you defined your class differently, but you didn't, and I'm not exactly sure
*why* someone is asking me for "__len__", so the safest thing to say is that
I don't have such an attribute".
Then the problem is that "just try it and see whether it works" code gets
written based on trying an example in a shell, to see whether AttributeError
or TypeError gets raised in the specific case the author is worried about,
and in a later release it raises the other one instead.
As an old-time Pythoneer, I took the almost total lack of "which exceptions
get raised when, exactly" docs as a warning that this stuff was *expected*
to change frequently, so I've always written "try it and see" code via
try:
it._and(see)
except (TypeError, AttributeError):
pass
That almost never "breaks" across releases.
Here's a specific 2.1 vs 2.2 example:
>>> for i in C(): pass # 2.1
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: C instance has no attribute '__getitem__'
>>>
>>> for i in C(): pass # 2.2a0
...
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: iter() of non-sequence
>>>
Since for-loops no longer require __getitem__ at all, even if we *could*
raise the same error in 2.2, it wouldn't make *sense* in 2.2.
In every case I've seen, a switch from AttributeError to TypeError makes
better sense in the end. Can break code, though!