On 7/31/2012 6:36 AM, Ulrich Eckhardt wrote:
Hi!

Using Python 2.7, I stumbled across the fact that 'self.xy' raises an
AttributeError if self doesn't have an 'xy' as attribute, but 'xy' will
instead raise a NameError. To some extent, these two are very similar,
namely that the name 'xy' couldn't be resolved in a certain context, but
they don't have a common baseclass.

You have observed a true fact. The two errors both involve non-reconition of identifiers. In most cases, the identifiers are looked-up in a dict.

I guess one of the reasons is behind the way that Python handles
variable lookup, the plain 'xy' will find local and global names while
'self.xy' will only look into onself. However, this vague idea is far
from enough to explain it to someone else.

Can someone help me out?

1. Why two separate exceptions:

a) operational: two separate exceptions are possible.

Name lookup and attribute lookup goes thru separate machinery. Name lookup may go thru local, nonlocal, global (modular) and builtin namespaces. Attribute lookup goes thru instance, class, and superclasses to object. Name lookup is fixed except for nonlocal and global declarations local assignments. Attribute lookup can be over-riden with special methods (and special method lookup skips the instance).

b) practical: two separate exceptions are desirable.

One catches an exception to do something in the except clause. Name and attribute errors often have different causes and different associated actions.

def empty_and_process(poppable, process):
  try:
    pop = popable.pop
  except AttributeError:
raise AttributeError("Can only empty_and_process objects with .pop method")

Whoops, programming typo raises NameError. It should *not* be caught, rather bug should be fixed.

Later usage error gets AttributeError, possibly documented. User decides whether it represents a bug to be fixed (the default) or a control signal to be caught and processed.

If one really does want the same action for the different mistakes, "except (NameError, AttributeError):" is easy enough to write.

2. Why no special base exception (NameAttrError ?).

Python exceptions started as strings with NO hierarchy. The current class system is still relatively flat. Flat is better than nested because nesting introduces a new entities and new entities should not be introduced without sufficient purpose and each one makes the whole harder to learn and remember. I suspect that if you grepped a large Python code corpus for this particular pair in except statements you would find it rare.

---
Another example: KeyError and IndexError are both subscript errors, but there is no SubscriptError superclass, even though both work thru the same mechanism -- __getitem__. The reason is that there is no need for one. In 'x[y]', x is usually intented to be either a sequence or mapping, but not possibly both. In the rare cases when one wants to catch both errors, one can easily enough. To continue the example above, popping an empty list and empty set produce IndexError and KeyError respectively:

  try:
    while True:
      process(pop())
  except (KeyError, IndexError):
    pass  # empty collection means we are done

--
Terry Jan Reedy



--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to