在 Bunch 类型对象中设置和获取属性

Set and Get attributes in Bunch type object

为简化 parse/create JSON,机器学习应用程序通常使用 Bunch 对象,例如https://github.com/dsc/bunch/blob/master/bunch/__init__.py

获取时,有一个嵌套的 EAFP 习语,它检查 dict.get() 函数,然后尝试使用字典方括号语法访问它,即

class Bunch(dict):
    def __getattr___(self, k):
        try:
            return object.__getattribute__(self, k)
        except AttributeError:
            try: 
                 return self[k]
            except KeyError:
                 raise AttributeError

并且在尝试设置属性时,

    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)

似乎 sklearn 实施遵循相同的思路,但检查较少 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

嵌套的 EAFP 似乎有点难以维护,我的问题是:

幸运的是,所有对象都有一个类似 dict 的内部对象来管理对象的属性(这在 __dict__ 属性中)。要执行您的要求,您只需让 class 将其自身用作 __dict__ 对象:

class Bunch(dict):
    def __init__(self, *args, **kwargs):
        self.__dict__ = self
        super().__init__(*args, **kwargs)

用法:

>>> b = Bunch()
>>> b.foo = 3
>>> b["foo"]
3
>>> b["foo"] = 5
>>> b.foo
5
>>> b["bar"] = 1
>>> b.bar
1