在类外部分配描述符属性

时间:2017-03-08 20:21:20

标签: python

我有一个数据存储延迟加载的描述符,如果数据存储不可用,则返回默认值。描述符工作正常,除了我希望默认值是提供的类类型的实例的情况,该类型是与定义描述符相同的类类型(树层次结构所需)。理想情况下我想做

from weakref import WeakKeyDictionary

class Descriptor(object):
    def __init__(self, classType=None):
        self.classType = classType
        self.values = WeakKeyDictionary()
    def __set_name__(self, owner, name):
        self.name = name
    def __get__(self, instance, owner):
        print("name:", self.name)
        if instance not in self.values:
            self.values[instance] = self.classType()
        return self.values[instance]
    def __set__(self, instance, value):
        self.values[instance] = value


class Item1(object):
    pass


class Item2(object):
    parent1 = Descriptor(Item1)
    parent2 = Descriptor(Item2)


item2 = Item2()
print(item2.parent1)
print(item2.parent2)

但很明显,parent2的描述符不能传递给它定义的类。所以另一种方法是在定义类后指定parent2:

class Item2(object):
    parent1 = Descriptor(Item1)


Item2.parent2 = Descriptor(Item2)

但是,在这种情况下,在创建构造函数时似乎没有调用__set_name__方法,因为给出了以下错误:

name: parent1
<__main__.Item1 object at 0x021F4890>
Traceback (most recent call last):
  File "..\setNameTest.py", line 28, in <module>
    print(item2.parent2)
  File "..\setNameTest.py", line 10, in __get__
    print("name:", self.name)
AttributeError: 'Descriptor' object has no attribute 'name'

在类中创建Descriptor之后,似乎只是将itemClass值分配给parent2,但在整个代码库中使用它似乎很笨拙:

class Item2(object):
    parent1 = Descriptor(Item1)
    parent2 = Descriptor()


Item2.__dict__['parent2'].classType = Item2

有更干净的方法吗?

编辑09Mar2017: 根据{{​​3}},如果实例为无,则从Descriptor返回self .__ get__可以清除此方法,即

    def __get__(self, instance, owner):
        if instance is None:
           return self
        print("name:", self.name)
        if instance not in self.values:
            self.values[instance] = self.classType()
        return self.values[instance]

现在可以通过

在类定义之外分配classType
Item2.parent2.classType = Item2

1 个答案:

答案 0 :(得分:0)

因此,当您需要在类级别范围内访问类本身时,必须使用元类。但是,我不熟悉新的__set_name__ dunder方法,而且我无法使用元类工作。然而,这是一种替代方式 - 旧方式 - 。首先,您必须将name传递给Descriptor __init__

In [10]: from weakref import WeakKeyDictionary
    ...:
    ...: class Descriptor(object):
    ...:     def __init__(self, name, classType=None):
    ...:         self.classType = classType
    ...:         self.values = WeakKeyDictionary()
    ...:         self.name = name
    ...:     def __get__(self, instance, owner):
    ...:         print("name:", self.name)
    ...:         if instance not in self.values:
    ...:             self.values[instance] = self.classType()
    ...:         return self.values[instance]
    ...:     def __set__(self, instance, value):
    ...:         self.values[instance] = value
    ...:
    ...:
    ...: class Item1(object):
    ...:     pass
    ...:

现在,定义您的元类。这可能是更好的封装,但它可以解决这个问题。请注意,我正在将name传递给Descriptor

In [11]: class ItemMeta(type):
    ...:     def __init__(cls, name, bases, dct):
    ...:         cls.parent1 = Descriptor('parent1', Item1)
    ...:         cls.parent2 = Descriptor('parent2', cls)
    ...:
    ...:

最后,使用Python3语法,我们使用元类创建Item2

In [12]: class Item2(object, metaclass=ItemMeta):
    ...:     pass
    ...:

现在,它应该有效:

In [13]: x = Item2()

In [14]: x.parent1
name: parent1
Out[14]: <__main__.Item1 at 0x1104db240>

In [15]: x.parent2
name: parent2
Out[15]: <__main__.Item2 at 0x1104dbc50>