如何在Python中添加多个类似的属性

时间:2014-02-02 18:13:11

标签: python properties descriptor

我正在构建一个模拟器,它将模拟各种类型的实体。所以我有一个基类ModelObject,并将为所有不同的实体使用子类。每个实体都有一组我想要跟踪的属性,所以我还有一个名为RecordedDetail的类,它保留了更改的轨迹(基本上构建了一个(time_step,value)对的列表)和每个ModelObject都有一个dict来存储这些内容。所以我有效地获得了

class ModelObject(object):
    def __init__(self):
        self.details = {}
        self.time_step = 0

    def get_detail(self, d_name):
        """ get the current value of the specified RecordedDetail"""
        return self.details[d_name].current_value()

    def set_detail(self, d_name, value):
        """ set the current value of the specified RecordedDetail"""
        self.details[d_name].set_value(value, self.time_step)


class Widget(ModelObject):
    def __init__(self):
        super().__init__(self)
        self.details["level"] = RecordedDetail()
        self.details["angle"] = RecordedDetail()

    @property
    def level(self):
        return self.get_detail("level")

    @level.setter
    def level(self, value):
        self.set_detail("level", value)

    @property
    def angle(self):
        return self.get_detail("angle")

    @angle.setter
    def angle(self):
        self.set_detail("angle", value)

这非常重复,我不禁想到必须有一种使用描述符自动化它的方法,但我无法弄清楚如何。我最终得到了

class RecordedProperty(object):
    def __init__(self, p_name):
        self.p_name = p_name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.get_detail(self.p_name)

    def __set__(self, instance, value):
        instance.set_detail(self.p_name, value)


class Widget(ModelObject):
    level = RecordedProperty("level")
    angle = RecordedProperty("angle")

    def __init__(self):
        super().__init__(self)
        self.details["level"] = RecordedDetail()
        self.details["angle"] = RecordedDetail()

这有点改进,但​​仍然有很多打字。

所以,几个问题。

  1. 我可以将描述符内容(__get____set__等)添加到RecordedDetail类中吗?这样做有什么好处吗?

  2. 在两个不同的地方有没有办法输入新属性名称(例如“级别”)少于三次?

  3. 我是否完全咆哮错误的树?

1 个答案:

答案 0 :(得分:2)

最后一段代码是在正确的轨道上。通过使用元类为列表中的每个项创建命名的RecordedProperty和匹配的RecordedDetail,可以使过程变得不那么讨厌。这是一个简单的例子:

class WidgetMeta(type):

    def __new__(cls, name, parents, kwargs):
        '''
        Automate the creation of the class
        '''

        for item in kwargs['_ATTRIBS']:
            kwargs[item] = RecordedProperty(item)
        return super(WidgetMeta, cls).__new__(cls, name, parents, kwargs)

class Widget(ModelObject):

    _ATTRIBS = ['level', 'angle']
    __metaclass__ = WidgetMeta

    def __init__(self, *args, **kwargs):
       super().__init__(self)
       self.Details = {}
       for detail in self._ATTRIBS:
           self.Details[detail] = RecordedDetail()

子类只需要在_ATTRIBS中包含不同的数据。

作为替代方案(我认为它更复杂)您可以使用元类来自定义 init ,就像您自定义 new 一样,创建RecordedDetails _ATTRIBS列表。

第三种选择是在第一次访问时在每个实例中创建RecordedDetail。只要你没有为每个属性都有RecordedDetail的代码,即使没有触及RecordedDetail,这也可以正常工作。

警告我对Python3不是很熟悉;我经常在2.7x

中使用上述模式