2014-04-12 37 views
5

使用python描述符時的常見設計模式是讓描述符保存使用該描述符的實例字典。例如,假設我要作出這樣的計算它的訪問次數的屬性:在不可相干的類中使用描述符 - python

class CountingAttribute(object): 

    def __init__(self): 
     self.count = 0 
     self.value = None 


class MyDescriptor(object): 

    def __init__(self): 
     self.instances = {} #instance -> CountingAttribute 

    def __get__(self, inst, cls): 
     if inst in self.instances: 
      ca = self.instances[inst] 
     else: 
      ca = CountingAttribute() 
      self.instances[inst] = ca 
     ca.count += 1 
     return ca 


class Foo(object): 
    x = MyDescriptor() 


def main(): 
    f = Foo() 
    f.x 
    f.x 
    print("f.x has been accessed %d times (including the one in this print)"%(f.x.count,)) 

if __name__ == "__main__": 
    main() 

這是一個完全愚蠢的例子,沒有做任何有用的東西;我試圖分離主要觀點。

的問題是,我不能在一個類,這是不是可哈希使用這個描述符,因爲該行

self.instances[inst] = ca 

使用情況作爲字典鍵。處理這種情況是否有明智的方式?例如,一個人立即想到使用實例的id,但我不確定這樣做是否會破壞應該如何使用散列。

編輯:我意識到instances應該是類似weakref.WeakKeyDictionary,但我試圖保持它簡單在這裏專注於可哈希性的問題。

+1

我的第一個想法是嘗試對'實例'使用['WeakKeyDictionary'](https://docs.python.org/3.4/library/weakref.html#weakref.WeakKeyDictionary)。不幸的是,這似乎並沒有解決潛在問題,但這仍是一個好主意。 (否則,描述符會使其他死對象保持活動狀態。) – icktoofay

+0

是的,在我的實際實現中,我確實使用了WeakKeyDictionary。我沒有這樣做,因爲我正在努力將重點放在眼前的問題上。 – DanielSank

回答

3

您可以使用id(inst)作爲關鍵。

請注意,這不包括一個對象被銷燬並且一個新對象使用新ID創建的情況。

爲了正確檢測到這個問題,您應該在字典中存儲ca weakref。如果您檢測到weakref的被引用對象已經消失,您必須假定給定的id被重用。

喜歡的東西

import weakref 

class MyDescriptor(object): 

    def __init__(self): 
     self.instances = {} #instance -> CountingAttribute 

    def __get__(self, inst, cls): 
     if inst is None: return self.instances # operating on the class, we get the dictionary. 
     i = id(inst) 
     if i in self.instances: 
      ca, wr = self.instances[i] 
      if wr() is None: del self.instances[i] 
     if i not in self.instances: 
      ca = CountingAttribute() 
      self.instances[i] = (ca, weakref.ref(inst)) 
     ca.count += 1 
     return ca 

這減輕從conntected到WeakKeyDictionary的hashability問題。

但也許你根本不需要字典。一種完全不同的方法可能是

class MyDescriptor(object): 

    def __get__(self, inst, cls): 
     if inst is None: return self, cls 
     try: 
      ca = inst.__the_ca 
     except AttributeError: 
      ca = inst.__the_ca = CountingAttribute() 
     ca.count += 1 
     return ca 

這種方法也有其不足之處。例如,你不能在一個類中多次使用這個描述符,也不會讓它變得醜陋。因此,它只能小心使用。第一個解決方案雖然更復雜,但卻是最簡單的解決方案。

+0

什麼是弱引用? :) – DanielSank

+0

@DanielSank編輯。 – glglgl

+0

這真的很聰明。 – DanielSank