簡體   English   中英

在類外部分配描述符屬性

[英]Assigning Descriptor attribute outside class

我有一個數據存儲延遲加載的描述符,如果數據存儲不可用,則返回默認值。 描述符工作正常,除了我希望默認值是提供的類類型的實例的情況,該類型是與定義描述符相同的類類型(樹層次結構所需)。 理想情況下我想做

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'

似乎工作的是在類中創建描述符之后將itemClass值分配給parent2,但在整個代碼庫中使用它似乎很笨重:

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


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

有更清潔的方法嗎?

編輯09Mar2017:根據https://stackoverflow.com/a/6995286/1834561 ,如果實例為None,則可以通過從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

因此,當您需要在類級別范圍內訪問類本身時,您必須使用元類 但是,我不熟悉新的__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>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM