Sometimes, you have a deeply nested set of attributes which may not be present somewhere along the chain. A try/except would be clunky.
>>> class Foo(object): pass
...
>>> f = Foo()
>>> f.a = f
>>> f.a.b = f
>>> f.a.b.c.d
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'c'
This is a simple class which allows an expression to be wrapped and unwrapped, with the result at the end being either the value obtained or None.
>>> class na(object):
... '''
... A None-Attributator object. Allows for "safe navigation",
... similar to the ?. attribute in C#, or undefined in JavaScript.
... '''
... __slots__ = ("base",)
... def __init__(self, base):
... self.base = base
... def __getattr__(self, name):
... return na(getattr(self.base, name, None))
... def __getitem__(self, key):
... try:
... return na(self.base[key])
... except (KeyError, TypeError, IndexError):
... return na(None)
... def __call__(self):
... return self.base
...
>>>
>>> na(f).a.b.c.d.e()
>>> na(f).a.b['cat']()