搜尋

首頁  >  問答  >  主體

Python, 理解下面这个装饰器(based on class), 有哪几个关键点 ?

class memoized_property(object):
    """A read-only @property that is only evaluated once."""

    def __init__(self, fget, doc=None):
        self.fget = fget
        self.__doc__ = doc or fget.__doc__
        self.__name__ = fget.__name__

    # 这个方法应该是这个缓存装饰器的关键
    # 因此, 我组织关键字如下
    #     * python __get__
    #     * how python __get__ works
    #     # python descript tools
    def __get__(self, obj, cls):
        if obj is None:
            return self
        obj.__dict__[self.__name__] = result = self.fget(obj)
        return result

    def _reset(self, obj):
        memoized_property.reset(obj, self.__name__)

    @classmethod
    def reset(cls, obj, name):
        obj.__dict__.pop(name, None)
怪我咯怪我咯2822 天前501

全部回覆(2)我來回復

  • PHP中文网

    PHP中文网2017-04-18 10:35:42

    根據memoized_property的實作方法,下面的答案都有一個前提,即假設其作為對類別函數的裝飾器來使用。此時這個類別可以看成是property裝飾器的修改版。 能夠實現快取的效果是因為Python存取屬性時是有優先權的

    對於a.val,Python進行如下處理:

    1. 先訪問物件的__dict__,即a.__dict__['val']

    2. 如果沒有再訪問類別的A.__dict__['val'],此時會沿著繼承關係一直向上尋找;

    3. 如果找到A.__dict__['val'],返回的是值的话,那么就获得该值;如果返回的是一个描述器,则会调用描述器的__get__,回傳的是值的話,那麼就得到該值;如果傳回的是一個描述器,則會呼叫描述器的

      方法;

    memoized_property對於這裡的

    來說:

    例如這個類別封裝了A類的val函數:

    class A(object):
        ...
        
        @memoized_property
        def val(self):
            ...
    
    a = A()
    a.val
    
    val的时候,根据上面的查找顺序:对象里面没有,跳到第二步;在类的字典里发现了,但发现是描述器,因此会进入到描述器中的__get__方法中。在这里,使用self.fget(obj)调用装饰的val函数并计算结果后,在返回结果的同时,将结果也存储在obj.__dict__['val']中。下次再访问a.val的时候,由于对象的__dict__中有val了,就会先查找obj.__dict__['val'],而不会大动干戈的去找__get__。这样就实现缓存一个属性的效果。而一般的__get__是不会设置obj.__dict__['xxx']在第一次造訪val的時候,根據上面的查找順序:物件裡面沒有,跳到第二步;在類別的字典裡發現了,但發現是描述器,因此會進入到描述器中的

    方法中。在這裡,使用self.fget(obj)呼叫裝飾的val函數並計算結果後,在傳回結果的同時,將結果也儲存在obj. __dict__['val']中。下次再造訪

    的時候,由於物件的__dict__中有val了,就會先找obj.__dict__['val'] ,而不會大動乾戈的去找reset就很清楚了,只不过把上一个优先级的途径去掉。然后Python就不得不沿着优先级一步步找下去,发现__get__可用,于是又在其中调用a.val。這樣就實現緩存一個屬性的效果。而一般的

    是不會設定obj.__dict__['xxx']的,所以每次都是重新計算。

    __get__明白了這些以後,reset就很清楚了,只不過把上一個優先級的途徑去掉。然後Python就得沿著優先權一步步找下去,發現

    可用,於是又在其中呼叫🎜方法重新計算了一遍。 🎜 🎜而🎜的內部,又能說好多了。 。 。 。 🎜

    回覆
    0
  • PHP中文网

    PHP中文网2017-04-18 10:35:42

    類別方法就是當你不用做類別的實例化就可以直接呼叫的方法

    回覆
    0
  • 取消回覆