Friday, May 2, 2014

NullAttributor

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']()