For simplicity to parse/create JSON, machine learning applications usually uses the Bunch
object, e.g. https://github.com/dsc/bunch/blob/master/bunch/__init__.py
When getting, there's a nested EAFP idiom that checks through the dict.get()
function and then trying to access it with dictionary square bracket syntax, i.e.
class Bunch(dict):
def __getattr___(self, k):
try:
return object.__getattribute__(self, k)
except AttributeError:
try:
return self[k]
except KeyError:
raise AttributeError
And when trying to set an attribute,
def __setattr__(self, k, v):
try:
# Throws exception if not in prototype chain
object.__getattribute__(self, k)
except AttributeError:
try:
self[k] = v
except:
raise AttributeError(k)
else:
object.__setattr__(self, k, v)
Seems like the sklearn
implementation follows the same train of thought but has lesser checks https://github.com/scikit-learn/scikit-learn/blob/2beed5584/sklearn/utils/__init__.py#L61
class Bunch(dict):
def __init__(self, **kwargs):
super().__init__(kwargs)
def __setattr__(self, key, value):
self[key] = value
def __dir__(self):
return self.keys()
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(key)
def __setstate__(self, state):
# Bunch pickles generated with scikit-learn 0.16.* have an non
# empty __dict__. This causes a surprising behaviour when
# loading these pickles scikit-learn 0.17: reading bunch.key
# uses __dict__ but assigning to bunch.key use __setattr__ and
# only changes bunch['key']. More details can be found at:
# https://github.com/scikit-learn/scikit-learn/issues/6196.
# Overriding __setstate__ to be a noop has the effect of
# ignoring the pickled __dict__
pass
The nested EAFP seems a little hard to maintain, my questions here are:
.update()
function work, shallow or deep copying? Or just let the default dict.update()
do what it does? Understanding dict.copy() - shallow or deep?
Lucky for you, all objects have an internal dict-like object that manages the attributes of the object (this is in the __dict__
attribute). To do what you're asking, you just need to make the class use itself as the __dict__
object:
class Bunch(dict):
def __init__(self, *args, **kwargs):
self.__dict__ = self
super().__init__(*args, **kwargs)
Usage:
>>> b = Bunch()
>>> b.foo = 3
>>> b["foo"]
3
>>> b["foo"] = 5
>>> b.foo
5
>>> b["bar"] = 1
>>> b.bar
1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With