Python:为类的变量添加一个属性,让它在赋值(描述符类)

时间:2016-11-03 15:24:57

标签: python

我们的项目从一堆不同的来源收集关于一个对象的大量信息,并且为了审计和解决我们的数据源之间的冲突,我们需要保留一堆关于每个属性的元数据

age = 21
age_metadata = {
     'source' : 'office gossip',
     'relability' : 0 
    {

问题是类开始看起来像这样。

class Person
    def __init__(self, id, ):
       self.id = id
       self.id_meta = None
       self.age = None
       self.age_meta = None
       self.name = None

看起来很难看,并且所有属性的数量加倍,所以我想要的是在每个属性下嵌套元数据。因此,如果我需要它,我可以拨打bob.age.meta而不是bob.age_meta,我希望这样做,同时保持属性或多或少不变。所以我想要一些总能让我使用它的东西:

def main():
    bob = Person(1)
    bob.age.meta = 'random metadata'
    bob.age = 30
    assert bob.age.meta == 'random metadata'

现在我认为应该可以使用描述符类的组合来拦截赋值+一些猴子修补/元分类魔术来保存元数据..但我是所有这些功能的新手并且很容易迷失。这是我当前的草图。从工作代码的方式:

class AttributeWithMeta(object):
    """interceot attribute fucntions"""

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        """take  attribute and somehow replace it with monkey patched version"""
        self._value = magic(value,meta)

    @staticmethod
    def magic(value, meta):
        """adds meta attribute to objects regardless of type"""
        return  value_with_meta


class Person(object):
    age = AttributeWithMeta()

    def __init__(self, id):
        self.id = id

我是朝着正确的方向前进的吗?有没有其他方法可以做到这一点,我错过了?我想在这里做不可能吗?

如果属性有问题,P.S最坏情况age.get_meta()是可以接受的。

1 个答案:

答案 0 :(得分:1)

bob.age.meta
   ^   ^
   |   |_______ This lookup can be managed by type(bob.age) class
   |
   |___________ This lookup can be managed by type(bob) class, i.e. Person

.meta的属性访问完全由bob.age对象处理,因此您的Person类在这里没有发言权。

通过在Person类上定义描述符,您可以控制设置/获取/删除age属性在Person实例上的行为方式。你在个人实例和属性之间own the dot,可以做任何你想要的事情。但是这种力量并没有达到第二个点 - 即.age之间的meta

如果您希望bob.age从字面上返回整数30,那么您将很难让bob.age.meta做任何事情而不是提升AttributeError。那是因为你现在需要在int类上定义一个描述符,并且在python中不可能使用猴子修补内置类型。无论如何,并非没有terrible and unreliable hacks

如果您对bob.age返回对象的某种容器和关联的元数据感到高兴,那么Person类的描述符可能是您想要的。但是如果你希望bob.age只返回对象,而bob.age.meta只返回元数据,那么我认为你正在咆哮错误的树。

一个不那么复杂的设计是每个Person实例直接有一个meta dict,这样你就可以查找例如bob.meta['age']来获取与bob {{1}相关的元数据。 1}}属性。