【问题标题】:How checking lookup depth into nested dictionary as class attribute?如何检查嵌套字典中的查找深度作为类属性?
【发布时间】:2021-05-11 22:04:19
【问题描述】:

我根据在那里找到的AttrDict 创建了一个嵌套字典:

Object-like attribute access for nested dictionary

我将其修改为在“叶子”中包含str 命令,这些命令在请求/写入值时执行:

commands = {'root': {'com': {'read': 'READ_CMD', 'write': 'WRITE_CMD'} } }

class AttrTest()
    def __init__:
        self.__dict__['attr'] = AttrDict(commands)

test = AttrTest()
data = test.attr.root.com.read    # data = value read with the command
test.attr.root.com.write = data   # data = value written on the com port

虽然效果很好,但我想:

  • 避免人们访问attr/root/com,因为这会返回一个子级字典
  • 直接访问attr.root.com的人(通过__getattribute__/__setattr__)

目前,我面临以下问题:

  • 如前所述,在访问嵌套字典的“主干”时,我得到了“叶子”的部分字典
  • 访问attr.root.com 时返回{'read': 'READ_CMD', 'write': 'WRITE_CMD'}
  • 如果检测到read,我会进行正向查找并返回值,但随后attr.root.com.read 失败

是否有可能知道 Python 将在“路径”中请求的最终级别是什么?

  • 阻止访问attr/root
  • 直接读取/写入访问attr.root.com的值(使用正向查找)
  • 仅在请求 attr.root.com.read 或 attr.root.com.write 时返回所需的部分字典

目前我没有发现任何东西可以让我控制预期的查找深度。

感谢您的考虑。

【问题讨论】:

    标签: python dictionary nested attributes


    【解决方案1】:

    对于给定的属性查找,您无法确定有多少其他属性会跟随;这就是 Python 的工作原理。为了解析x.y.z,首先需要检索对象x.y,然后才能执行后续的属性查找(x.y).z。

    但是,您可以做的是返回一个代表(部分)路径的代理对象,而不是存储在 dict 中的实际底层对象。因此,例如,如果您执行了test.attr.com,那么这将返回一个代理对象,该对象表示要在test 对象上查找的路径attr.com。只有在路径中遇到read 或write 叶时,才会解析路径并读取/写入数据。

    以下是一个示例实现,它使用基于__getattr__ 的AttrDict 来提供Proxy 对象(因此您不必拦截__getattribute__):

    from functools import reduce
    
    
    class AttrDict(dict):
        def __getattr__(self, name):
            return Proxy(self, (name,))
    
        def _resolve(self, path):
            return reduce(lambda d, k: d[k], path, self)
    
    
    class Proxy:
        def __init__(self, obj, path):
            object.__setattr__(self, '_obj', obj)
            object.__setattr__(self, '_path', path)
    
        def __str__(self):
            return f"Path<{'.'.join(self._path)}>"
    
        def __getattr__(self, name):
            if name == 'read':
                return self._obj._resolve(self._path)[name]
            else:
                return type(self)(self._obj, (*self._path, name))
    
        def __setattr__(self, name, value):
            if name != 'write' or name not in (_dict := self._obj._resolve(self._path)):
                raise AttributeError(f'Cannot set attribute {name!r} for {self}')
            _dict[name] = value
    
    
    commands = {'root': {'com': {'read': 'READ_CMD', 'write': 'WRITE_CMD'} } }
    
    test = AttrDict({'attr': commands})
    print(f'{test.attr = !s}')                # Path<attr>
    print(f'{test.attr.root = !s}')           # Path<attr.root>
    print(f'{test.attr.root.com = !s}')       # Path<attr.root.com>
    print(f'{test.attr.root.com.read = !s}')  # READ_CMD
    test.attr.root.com.write = 'test'
    test.attr.root.write = 'illegal'  # raises AttributeError
    

    【讨论】:

      猜你喜欢
      • 2011-02-01
      • 2018-01-03
      • 2021-12-29
      • 2014-06-10
      • 2021-07-16
      • 2019-07-12
      • 2020-12-10
      • 1970-01-01
      • 2013-09-05
      相关资源
      最近更新 更多