动态添加属性到Python类,指向字典中的项目

0 人关注

我正试图将属性动态地附加到一个类( Registry )上,以便于访问一个字典中的值。我使用 defaultdict 来定义字典,默认值是一个空的 list

但由于我在定义属性时访问字典中的列表值的方式,我最终发现所有属性都指向同一个列表对象。

Gist: https://gist.github.com/subhashb/adb75a3a05a611c3d9193da695d46dd4

from collections import defaultdict from enum import Enum class ElementTypes(Enum): PHONE = "PHONE" CAR = "CAR" class Registry: def __new__(cls, *args, **kwargs): cls.setup_properties() instance = super(Registry, cls).__new__(cls, *args, **kwargs) return instance def __init__(self): self._elements = {} def register(self, element_type, item): if element_type.value not in self._elements: self._elements[element_type.value] = [] self._elements[element_type.value].append(item) def get(self, element_type): return self._elements[element_type.value] @classmethod def setup_properties(cls): for item in ElementTypes: prop_name = item.value prop = property(lambda self: getattr(self, "get")(item)) setattr(Registry, prop_name.lower(), prop) registry = Registry() registry.register(ElementTypes.PHONE, "phone1") registry.register(ElementTypes.PHONE, "phone2") registry.register(ElementTypes.CAR, "car1") registry.register(ElementTypes.CAR, "car2") assert dict(registry._elements) == { "CAR": ["car1", "car2"], "PHONE": ["phone1", "phone2"], assert hasattr(registry, "phone") assert hasattr(registry, "car") assert registry.car == ["car1", "car2"] assert registry.phone == ["phone1", "phone2"] # This fails

我如何将属性中的代码定义为真正的动态,并在dict中获得对单个列表值的访问?

1 个评论
你把事情搞得太复杂了。你所需要的只是一个带点标记的dict包装器,如 this or (还有更多的例子,只要搜索 "python dict dot notation "就可以了)
python
python-3.x
Subhash
Subhash
发布于 2020-08-28
1 个回答
Jasmijn
Jasmijn
发布于 2020-08-28
已采纳
0 人赞同

首先,不要在 __new__ 中设置属性,那会为每一个创建的 Registry 对象被调用!而是在类定义之外分配属性。相反,只需在类定义之外分配属性。

第二,这让很多人感到困惑,但是如果你在for-loop里面使用一个 lambda ,并且你想使用 item 变量。你需要确保你添加了一个叫做 item 的参数,其默认值为 item ,否则所有的属性都会指向循环的最后一个 item

class Registry:
    def __init__(self):
        self._elements = defaultdict(list)
    def register(self, element_type, item):
        self._elements[element_type.value].append(item)
    def get(self, element_type):
        return self._elements[element_type.value]
for item in ElementTypes: