I'm a huge fan of Python3's types.SimpleNamespace. Basically, it allows us to take a dict, like so:
my_dict = {
"a": 1,
"b": 2,
"c": 3,
}
my_dict["a"] # 1
my_dict["b"] # 2, etc
and manage it like this:
from types import SimpleNamespace
my_namespace = SimpleNamespace(a=1, b=2, c=3)
my_namespace.a # 1
my_namespace.b # 2
my_namespace.c # 3
Alternatively, we could also do something like:
from types import SimpleNamespace
my_dict = {
"a": 1,
"b": 2,
"c": 3,
}
my_namespace = SimpleNamespace(**my_dict)
my_namespace.a # 1
my_namespace.b # 2
my_namespace.c # 3
But - what happens if our my_dict is nested? Like so:
my_dict = {
"a": {
"d": 4,
},
"b": 2,
"c": 3,
"e": [5,6,7,{
"f": 8,
}]
}
my_namespace = SimpleNamespace(**my_dict)
my_namespace.a # {"d": 4} /womp womp 😭
my_namespace.a.d # raises Exception! 😭😭
In short, SimpleNamespace simply does not support this use case. But that's ok! We can extend SimpleNamespace and build this functionality for ourselves.
Defining RecursiveNamespace
from types import SimpleNamespace
# this is how SimpleNamespace looks when output
SimpleNamespace(**my_dict)
# namespace(a={'d': 4}, b=2, c=3, e=[5, 6, 7, {'f': 8}])
class RecursiveNamespace(SimpleNamespace):
@staticmethod
def map_entry(entry):
if isinstance(entry, dict):
return RecursiveNamespace(**entry)
return entry
def __init__(self, **kwargs):
super().__init__(**kwargs)
for key, val in kwargs.items():
if type(val) == dict:
setattr(self, key, RecursiveNamespace(**val))
elif type(val) == list:
setattr(self, key, list(map(self.map_entry, val)))
# this is how RecursiveNamespace looks when output
RecursiveNamespace(**my_dict)
# RecursiveNamespace(
# a=RecursiveNamespace(d=4),
# b=2,
# c=3,
# e=[5, 6, 7, RecursiveNamespace(f=8)])
So, what's happening here? We establish a new class, RecursiveNamespace that extends SimpleNamespace. In the __init__ constructor method, we call SimpleNamespace's constructor. Then, we just walk through our dictionary and for value that is also a dictionary or list, we instantiate that with RecursiveNamespace. Ta da.
P.S.
Technically, we don't even really need types.SimpleNamespace here - we can implement this class without by just adding two lines of code:
class RecursiveNamespace2: # without extending SimpleNamespace!
@staticmethod
def map_entry(entry):
if isinstance(entry, dict):
return RecursiveNamespace(**entry)
return entry
def __init__(self, **kwargs):
for key, val in kwargs.items():
if type(val) == dict:
setattr(self, key, RecursiveNamespace(**val))
elif type(val) == list:
setattr(self, key, list(map(self.map_entry, val)))
else: # this is the only addition
setattr(self, key, val)
Top comments (1)
Thank you for your post!
If your dictionary is serializable, you might as well use the json module to accomplish the same 'nested' effect: