NameError vs AttributeError

Discussion in 'Python' started by Ulrich Eckhardt, Jul 31, 2012.

  1. 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.

    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?

    Uli
     
    Ulrich Eckhardt, Jul 31, 2012
    #1
    1. Advertising

  2. Ulrich Eckhardt

    Terry Reedy Guest

    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
     
    Terry Reedy, Jul 31, 2012
    #2
    1. Advertising

  3. Ulrich Eckhardt

    Terry Reedy Guest

    On 7/31/2012 4:49 PM, Chris Kaynor wrote:
    > On Tue, Jul 31, 2012 at 1:21 PM, Terry Reedy <
    > <mailto:>> wrote:
    >
    > 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
    >
    > There is a base type for KeyError and IndexError: LookupError.
    >
    > http://docs.python.org/library/exceptions.html#exception-hierarchy


    Oh, so there is. Added in 1.5 strictly as a never-directly-raised base
    class for the above pair, now also directly raised in codecs.lookup. I
    have not decided if I want to replace the tuple in the code in my book.

    --
    Terry Jan Reedy
     
    Terry Reedy, Aug 1, 2012
    #3
  4. Ulrich Eckhardt

    Ethan Furman Guest

    Terry Reedy wrote:
    > On 7/31/2012 4:49 PM, Chris Kaynor wrote:
    >> On Tue, Jul 31, 2012 at 1:21 PM, Terry Reedy wrote:
    >>> 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
    >>>

    >> There is a base type for KeyError and IndexError: LookupError.
    >>
    >> http://docs.python.org/library/exceptions.html#exception-hierarchy

    >
    > Oh, so there is. Added in 1.5 strictly as a never-directly-raised base
    > class for the above pair, now also directly raised in codecs.lookup. I
    > have not decided if I want to replace the tuple in the code in my book.


    I think I'd stick with the tuple -- LookupError could just as easily
    encompass NameError and AttributeError.
     
    Ethan Furman, Aug 1, 2012
    #4
  5. Ulrich Eckhardt

    Terry Reedy Guest

    On 8/1/2012 11:53 AM, Ethan Furman wrote:
    > Terry Reedy wrote:
    >> On 7/31/2012 4:49 PM, Chris Kaynor wrote:
    >>> On Tue, Jul 31, 2012 at 1:21 PM, Terry Reedy wrote:


    >>>> 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
    >>>>
    >>> There is a base type for KeyError and IndexError: LookupError.
    >>>
    >>> http://docs.python.org/library/exceptions.html#exception-hierarchy

    >>
    >> Oh, so there is. Added in 1.5 strictly as a never-directly-raised base
    >> class for the above pair, now also directly raised in codecs.lookup. I
    >> have not decided if I want to replace the tuple in the code in my book.

    >
    > I think I'd stick with the tuple -- LookupError could just as easily
    > encompass NameError and AttributeError.


    Thank you. Having to remember exactly which lookup error is encompassed
    by LookupError illustrates my point about the cost of adding entities
    without necessity. It also illustrates the importance of carefull
    naming. SubscriptError might have been better.

    --
    Terry Jan Reedy
     
    Terry Reedy, Aug 1, 2012
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. GMTaglia

    NameError

    GMTaglia, Oct 5, 2004, in forum: Python
    Replies:
    2
    Views:
    2,744
    GMTaglia
    Oct 5, 2004
  2. seancron

    nameerror upon calling function

    seancron, Sep 1, 2007, in forum: Python
    Replies:
    1
    Views:
    354
    Arnaud Delobelle
    Sep 1, 2007
  3. Replies:
    10
    Views:
    463
  4. jolly
    Replies:
    2
    Views:
    578
    jolly
    Dec 19, 2007
  5. Replies:
    3
    Views:
    606
    Dan Bishop
    Mar 22, 2008
Loading...

Share This Page