代码之家  ›  专栏  ›  技术社区  ›  Anton Melnikov

Python:缓存描述符的值

  •  3
  • Anton Melnikov  · 技术社区  · 10 年前

    我想澄清一下Python描述符的一些问题。我想用一些复杂的设置/获取机制向类中添加一个属性,并将这些计算值缓存在描述符对象中。简化示例如下:

    class Pro(object):
        """My descriptor class"""
    
        def __init__(self):
            self.value = None
    
        def __get__(self, instance, owner):
            return self.value
    
        def __set__(self, instance, value):
            self.value = value
    
    
    class Something(object):
        """My simple class"""
        pro = Pro()
    
    a = Something()
    a.pro = 1
    
    b = Something()
    b.pro = 2
    
    print(a.pro, b.pro) # At first, I've expected them to be 1 and 2
    

    我想不知怎么的 pro 属性将是的唯一实例 Pro 对于 Something 显然我错了。看起来我应该用类似的东西 instance._value 而不是 self.value 在…内 __set__ __get__ 但我真的希望把一切都藏在里面 赞成 班这是可能的吗?谢谢

    1 回复  |  直到 10 年前
        1
  •  4
  •   Ashwini Chaudhary    10 年前

    代码的问题是,您正在设置Pro实例的属性,该属性将由所有实例共享 Something 。若要解决此问题,应在 某物 ,一种方法是使用元类:

    class Meta(type):
        def __new__(cls, name, bases, dct):
            for k, v in dct.items():
                if isinstance(v, Pro):
                    # add an _ in front of the name
                    v.name = '_' + k
            return super(Meta, cls).__new__(cls, name, bases, dct)
    
    
    class Pro(object):
    
        def __get__(self, ins, typ):
            return getattr(ins, self.name)
    
        def __set__(self, ins, val):
                setattr(ins, self.name, val)
    
    class Something(object):
        """My simple class"""
        __metaclass__ = Meta
        pro = Pro()
    
    a = Something()
    a.pro = 1
    
    b = Something()
    b.pro = 2
    

    演示:

    >>> a.pro, b.pro
    (1, 2)
    >>> a.__dict__
    {'_pro': 1}
    >>> b.__dict__
    {'_pro': 2}
    >>> a.pro = 100
    >>> a.__dict__
    {'_pro': 100}
    

    因此,无法在Something中创建隐藏属性 实例,对吗?

    不,有。你可以在 Pro 的实例,该实例存储与 某物 。例如,如果 某物 的实例是可散列的,然后可以使用 weakref.WeakKeyDictionary 这个 WeakKeyDictionary 将确保 某物 的实例没有剩余的引用,那么它将立即被垃圾收集,这在正常情况下是不可能的 dict :

    from weakref import WeakKeyDictionary
    
    class Pro(object):
    
        def __init__(self):
            self.instances = WeakKeyDictionary()
    
        def __get__(self, ins, typ):
            return self.instances[ins]
    
        def __set__(self, ins, val):
            self.instances[ins] = val
    
    p = Pro()
    
    class Something(object):
        """My simple class"""
        pro = p
    
    a = Something()
    a.pro = 1
    
    b = Something()
    b.pro = 2
    
    print a.pro, b.pro
    
    print p.instances.items()
    del a
    print p.instances.items()
    

    输出:

    1 2
    [(<__main__.Something object at 0x7fb80d0d5310>, 1), (<__main__.Something object at 0x7fb80d0d5350>, 2)]
    [(<__main__.Something object at 0x7fb80d0d5350>, 2)]