Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set and Get attributes in Bunch type object

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:

  • Is there a simpler way to handle get and set functions for Bunch data objects?
  • Are there any other Dict like object that allows mutability between attributes and keys?
  • How should Bunch object's .update() function work, shallow or deep copying? Or just let the default dict.update() do what it does? Understanding dict.copy() - shallow or deep?
like image 372
alvas Avatar asked Oct 15 '22 20:10

alvas


1 Answers

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
like image 144
Elan-R Avatar answered Oct 22 '22 03:10

Elan-R