Python 如何将查找深度作为类属性检查到嵌套字典中?

Python 如何将查找深度作为类属性检查到嵌套字典中?,python,dictionary,nested,attributes,Python,Dictionary,Nested,Attributes,我根据在那里找到的AttrDict创建了一个嵌套字典: 我将其修改为在“leaves”中包含str命令,该命令在请求/写入值时执行: commands = {'root': {'com': {'read': 'READ_CMD', 'write': 'WRITE_CMD'} } } class AttrTest() def __init__: self.__dict__['attr'] = AttrDict(commands) test = AttrTest() d

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

我将其修改为在“leaves”中包含
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(通过
    获取属性
    /
    设置属性
目前,我面临以下问题:

  • 如上所述,当访问嵌套dict的“trunk”时,我得到了“leaves”的部分dict
  • 访问
    attr.root.com
    时,它返回
    {'read':'read\u CMD','write':'write\u CMD'}
  • 如果检测到
    读取
    ,我将进行正向查找并返回值,但是
    attr.root.com.read
    失败
是否有可能知道Python在“路径”中请求的最终级别是什么

  • 阻止访问
    attr
    /
    root
  • 直接访问
    attr.root.com
    读取/写入值(使用正向查找)
  • 仅当请求
    attr.root.com.read
    attr.root.com.write
    时才返回所需的部分dict
目前,我还没有发现任何东西可以让我控制查找的深度


感谢您的考虑。

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

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

以下是一个示例实现,它使用基于
\uuuu getattr\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

从functools导入reduce
类别属性(dict):
def _ugetattr _;(self,name):
返回代理(self,(name,))
定义解析(自身、路径):
返回归约(λd,k:d[k],路径,自)
类代理:
定义初始化(自、对象、路径):
对象.uuuu setattr_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
对象。uuuu setattr_uuuuuuuuuu(自,u路径,u路径)
定义(自我):
返回f“路径”
def _ugetattr _;(self,name):
如果name=='read':
返回self.\u obj.\u解析(self.\u路径)[名称]
其他:
返回类型(self)(self.\u对象,(*self.\u路径,名称))
定义设置属性(自身、名称、值):
如果名称!='在(_dict:=self.\u obj.\u resolve(self.\u path))中写入“或名称”:
raise AttributeError(f'无法为{self}'设置属性{name!r})
_dict[name]=值
命令={'root':{'com':{'read':'read_CMD','write':'write_CMD'}}
test=AttrDict({'attr':commands})
打印(f'{test.attr=!s}')#路径
打印(f'{test.attr.root=!s}')#路径
打印(f'{test.attr.root.com=!s}')#路径
打印(f'{test.attr.root.com.read=!s}')#read_CMD
test.attr.root.com.write='test'
test.attr.root.write='非法'#引发AttributeError
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