2011-11-08 47 views
1

我已經讓自己成爲一個懶惰的變量類,並在另一個類中使用它。我怎樣才能訪問惰性變量類的屬性?我已經嘗試__getattr__沒有運氣。這裏有一個例子:如何訪問惰性變量類的屬性?

class lazyobject(object): 
    def __init__(self,varname,something='This is the something I want to access'): 
     self.varname = varname 
     self.something = something 

    def __get__(self, obj, type=None): 
     if obj.__dict__.has_key(self.varname): 
      print "Already computed %s" % self.varname 
      return obj.__dict__[self.varname] 
     else: 
      print "computing %s" % self.varname 
      obj.__dict__[self.varname] = "something else" 
      return obj.__dict__[self.varname] 

class lazyobject2(lazyobject): 
    def __getattr__(self): 
     return self.something 

class dummy(object): 
    def __init__(self): 
     setattr(self.__class__, 'lazy', lazyobject('lazy')) 

class dummy2(object): 
    def __init__(self): 
     setattr(self.__class__, 'lazy', lazyobject2('lazy')) 

d1 = dummy() 
d2 = dummy2() 

try: 
    print "d1.lazy.something - no getattr: ",d1.lazy.something 
except: 
    print "d2.lazy is already computed - can't get its .something because it's now a string!" 
print "d1.lazy - no getattr: ",d1.lazy 

try: 
    print "d2.lazy.something - has getattr: ",d2.lazy.something 
except: 
    print "d2.lazy is already computed - can't get its .something because it's now a string!" 
print "d2.lazy - no getattr: ",d2.lazy 

此打印:

d1.lazy.something - no getattr: computing lazy 
d2.lazy is already computed - can't get its .something because it's now a string! 
d1.lazy - no getattr: something else 
d2.lazy.something - has getattr: computing lazy 
d2.lazy is already computed - can't get its .something because it's now a string! 
d2.lazy - no getattr: something else 

我想它打印:

d1.lazy.something - no getattr: This is the something I want to access 
computing lazy 
d1.lazy - no getattr: something else 

上面的例子是人爲的,但我希望跨獲取點。另一種解釋我的問題的方法是:如何在訪問類屬性時繞過__get__方法?

回答

4

訪問類屬性時,繞過__get__的方法是通過類字典而不是使用虛線訪問來查找它。

這很容易用功能對象來演示。例如:

>>> class A(object): 
     def f(self): 
      pass 

>>> A.f       # dotted access calls f.__get__ 
<unbound method A.f> 
>>> vars(A)['f']    # dict access bypasses f.__get__ 
<function f at 0x101723500> 

>>> a = A() 
>>> a.f       # dotted access calls f.__get__ 
<bound method A.f of <__main__.A object at 0x10171e810>> 
>>> vars(a.__class__)['f']  # dict access bypasses f.__get__ 
<function f at 0x101723500> 

另一塊你丟失的信息是,之前__getattr__這隻有在沒有屬性被發現運行繼承__get__運行。該邏輯由__getattribute__控制,該繼承自對象。所以,如果你想繞過__get__你需要在子類中編寫一個新的__get__或者通過在子類中定義__getattribute__來改變查找邏輯。

要解決的lazyobject2類,更換__getattr__有:

class lazyobject2(lazyobject): 

    def __getattribute__(self, key): 
     # bypass __get__ 
     return object.__getattribute__(self, '__dict__')[key] 

綜上所述,用來解決這個問題認識的關鍵部分是:

  • object.__getattribute__控制查找邏輯。
  • 它首先尋找__get__是否在當前類中定義或繼承。
  • 只有在找不到任何東西的情況下,纔會嘗試致電object.__getattr__
  • 以上三個步驟僅適用於虛線查找。
  • 那些步驟可以通過直接訪問字典通過__dict__vars()繞過。

描述符邏輯的全部細節可以在this writeupthis presentation中找到。