How do I return None instead of raising AttributeError?

Tim Peters tim_one at email.msn.com
Sun Sep 24 08:48:59 CEST 2000


[Scott M. Ransom]
> I want a simple class that returns None if I try and access any
> attribute that has not already been set.  In other words, I want to
> return a None instead of raising an AttributeError.

It's your throat <0.7 wink>.

> Here is try number one:
>
> >>> class test1:
> ...     def __getattr__(self, name):
> ...         if self.__dict__.has_key(name):
> ...             return self.name
> ...         else:
> ...             return None
> ...
> >>> a = test1()
> >>> a.b = 1.0
> >>> a.b
> 1.0
> >>> a.c
> >>> a
> Traceback (most recent call last):
>   File "<stdin>", line 1, in ?
> TypeError: call of non-function (type None)
>
> This gives me (almost) the correct functionality except that I can't use
> any of the default class methods!

Well, __getattr__ can't read your mind.  This does exactly what you said you
wanted:  "returns None if I try and access any attribute that has not
already been set"; you didn't set "__repr__", and it returned None.

Defining __getattr__ at all announces that *you* want to be responsible for
name lookups.  You want the power, you assume the responsibility.

BTW, note that __getattr__ isn't called at all for attributes that *do*
explicitly exist, so checking for self.__dict__.has_key(name) here isn't
doing anything for you (it will never be true).  That is, the definition
above is the same as

    def __getattr__(self, name):
        return None

> The last line passes '__repr__' as name, which isn't in __dict__ and
> so a None gets returned.
>
> Try again:
>
> >>> class test2:
> ...     def __getattr__(self, name):
> ...         try: return self.name
> ...         except AttributeError:
> ...             return None
> ...
> >>> a = test2()
> >>> a.b = 1.0
> >>> a.b
> 1.0
> >>> a.c
> Segmentation fault
>
> Now a SegFault!  Any idea what is happening here?  (I am using Python
> 1.6 under Linux...)

It's unbounded recursion:  a.c calls __getattr__ with "c", which tries
looking up self.name, which calls __getattr__ with "name", which tries
looking up self.name, which calls __getattr__ with "name", and so on, and so
on.  You eventually run out of stack space and the C runtime blows up.  A
lot of crap has been added to Python 2.0 to try and catch this potential
stack overflow on various platforms, and give you a message about it instead
of letting the platform dump core.

Back to your problem, like I said, Python can't read your mind.  Safest
would be for you to enumerate the exact list of "default class methods" you
want to have *not* return None when they're not explicitly defined.  Since
"default class method" isn't a well-defined concept in Python, nobody can do
that for you (because we can't read your mind either <wink>).

As an approximation, the names of most special methods both begin and end
with two underscores, so you can special-case that:

class test1:
    def __getattr__(self, name):
        if name[:2] == "__" == name[-2:]:
            raise AttributeError(name)

For things like __repr__, Python first tries
instance.__getattr__("__repr__"), and if that raises an exception *then* it
backs off to the default __repr__.  So this works as you hope, so far as
your examples got:

>>> a = test1()
>>> a.b = 1
>>> a.b
1
>>> a.c
>>> a
<__main__.test1 instance at 0250B56C>
>>>

But note the next one:

>>> len(a)
Traceback (innermost last):
  File "<pyshell#24>", line 1, in ?
    len(a)
  File "C:/Python20/test.py", line 16, in __getattr__
    raise AttributeError(name)
AttributeError: __len__
>>>

If you *truly* wanted every non-existent attr to return None, that last line
should  blow up with some bizarre error like "call of non-function None"
instead.

Again, since you're inventing a language that's not Python in its behavior,
__getattr__ will *let* you do that, but it's up to you to define every case
the way you want it to work.

that-something-can-be-done-is-not-an-invitation-to-do-it-ly y'rs  - tim






More information about the Python-list mailing list