Skip to content

Instantly share code, notes, and snippets.

@dhilst
Created April 7, 2019 18:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhilst/121fe0d963e7e9c8c4402eb4e0863b2f to your computer and use it in GitHub Desktop.
Save dhilst/121fe0d963e7e9c8c4402eb4e0863b2f to your computer and use it in GitHub Desktop.
Create open objects with easy, access dicts as objects and arrays with a simple notation
from collections.abc import MutableMapping
class O(MutableMapping):
'''
O class make possible to create deep nested objects without struggle.
Just instantiate it!
>>> o = O()
It's represented by its contructor. This means that coping and pasting
the representation of the object will cosntruct a similar object.
>>> o
O()
So evalutating its represenation handles to
an equivalent object. Also is able to compare by equality.
>>> eval(repr(o)) == O()
True
When you access an attribute a new nested O is created!
>>> o.a
O()
>>> o
O(a=O())
You can save attributes as expected!
>>> o.a.b = 10
And of course retrieve them again!
>>> o.a.b
10
>>> o
O(a=O(b=10))
O can be converted to a plain object, it will be a list or dict depending on the root type
>>> o.to_plain()
{'a': {'b': 10}}
Also you can construct it from a dict
>>> O(**{'a': {'b': 10, 'c': '20'}})
O(a=O(b=10, c='20'))
Arrays are supported
>>> o = O(*['a', 'b', 'c'])
>>> o
O('a', 'b', 'c')
>>> o.to_plain()
['a', 'b', 'c']
Array form also respect the evaluable repr
>>> eval(repr(o)) == o
True
Array index are transparent
>>> O(*['a'])[0]
'a'
Mixing arrays and dics
>>> o = O(*['a', 'b', {'c': {'d': [1, 2, {'e': {'f': 'g'}}]}}])
>>> o
O('a', 'b', O(c=O(d=O(1, 2, O(e=O(f='g'))))))
Iterating over array works as expected
>>> sum(O(1, 2, 3))
6
But if you mix then in the same level, dict has precendence.
>>> O('a', 'b', foo='bar').to_plain()
{'foo': 'bar', '0': 'a', '1': 'b'}
You can use it to access request data easier
>>> # import requests
>>> # data = requests.get('https://catfact.ninja/facts?limit=3&max_length=140').json()
>>> response = {'current_page': 1,
... 'data': [{'fact': 'In relation to their body size, cats have the largest eyes '
... 'of any mammal.',
... 'length': 73},
... {'fact': 'The Pilgrims were the first to introduce cats to North '
... 'America.',
... 'length': 63},
... {'fact': 'Ancient Egyptian family members shaved their eyebrows in '
... 'mourning when the family cat died.',
... 'length': 91}],
... 'from': 1,
... 'last_page': 76,
... 'next_page_url': 'https://catfact.ninja/facts?page=2',
... 'per_page': '3',
... 'prev_page_url': None,
... 'to': 3,
... 'total': 226}
>>> oresp = O(**response)
>>> oresp.data[2].length
91
'''
def __init__(self, *args, **kwargs):
self.__dict__['_child'] = {}
for k, v in kwargs.items():
if isinstance(v, dict):
setattr(self, k, O(**v))
elif isinstance(v, list):
setattr(self, k, O(*v))
else:
setattr(self, k, v)
for i, v in enumerate(args):
try:
setattr(self, str(i), O(**v))
except TypeError:
setattr(self, str(i), v)
def __getattr__(self, attr):
return self._child.setdefault(attr, O())
def __setattr__(self, attr, val):
self._child[attr] = val
def __repr__(self):
if any(not k.isnumeric() for k in self._child.keys()):
return '{}({})'.format(self.__class__.__name__,
', '.join(f'{k}={repr(v)}' for k, v in self._child.items()))
return '{}({})'.format(self.__class__.__name__,
', '.join(repr(v) for v in self._child.values()))
def __eq__(self, other):
if issubclass(type(other), O):
return self.to_plain() == other.to_plain()
def __delitem__(self, item):
return self._child.__delitem__(item)
def __getitem__(self, item):
return getattr(self, str(item))
def __setitem__(self, item, val):
return self._child.__setitem__(item, val)
def __iter__(self):
if any(not k.isnumeric() for k in self._child.keys()):
return self._child.__iter__()
return iter(self._child.values())
def __len__(self):
if any(not k.isnumeric() for k in self._child.keys()):
return self._child.__len__()
return len(self._child.values())
def to_plain(self):
if any(not k.isnumeric() for k in self._child.keys()):
return {k: v.to_plain() if issubclass(type(v), O) else v for k, v in self._child.items()}
return [v.to_plain() if issubclass(type(v), O) else v for v in self._child.values()]
if __name__ == '__main__':
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment