2012-02-06 80 views
4

我在寫一個python類,它使用__setattr____getattr__來提供自定義屬性訪問。使用__setattr__和描述符爲python類

但是,某些屬性不能用通用的方式處理,所以我希望爲這些屬性使用描述符。

產生了一個問題,對於一個描述符,描述符的__get__將對該情況__getattr__的調用,而是分配給的屬性時,將__setattr__有利於描述__set__的調用。

一個例子:

class MyDesc(object): 

    def __init__(self): 
     self.val = None 

    def __get__(self, instance, owner): 
     print "MyDesc.__get__" 
     return self.val 

    def __set__(self, instance, value): 
     print "MyDesc.__set__" 
     self.val = value 

class MyObj(object): 

    foo = MyDesc() 

    def __init__(self, bar): 
     object.__setattr__(self, 'names', dict(
      bar=bar, 
     )) 
     object.__setattr__(self, 'new_names', dict()) 

    def __setattr__(self, name, value): 
     print "MyObj.__setattr__ for %s" % name 
     self.new_names[name] = value 

    def __getattr__(self, name): 
     print "MyObj.__getattr__ for %s" % name 

     if name in self.new_names: 
      return self.new_names[name] 

     if name in self.names: 
      return self.names[name] 

     raise AttributeError(name) 

if __name__ == "__main__": 
    o = MyObj('bar-init') 

    o.bar = 'baz' 
    print o.bar 

    o.foo = 'quux' 
    print o.foo 

打印:

MyObj.__setattr__ for bar 
MyObj.__getattr__ for bar 
baz 
MyObj.__setattr__ for foo 
MyDesc.__get__ 
None 

描述符的__set__永遠不會被調用。

由於__setattr__定義不只是壓倒一切行爲一組有限的名字,還有,它可以推遲到object.__setattr__

不明白的地方是否有必須分配給屬性使用描述一個推薦的方式,如果可用,並且__setattr__否則?

回答

3

我想我會通過具有一種機制來自動標記這是 描述符中的每個班級,並以某種方式包裹__setattr__,它會打電話 對象的正常行爲對於那些名字接近這一點。

這可以用元類(和__setattr__

def setattr_deco(setattr_func): 
    def setattr_wrapper(self, attr, value): 
     if attr in self._descriptors: 
      return object.__setattr__(self, attr, value) 
     return setattr_func(self, attr, value) 
    return setattr_wrapper 

class MiscSetattr(type): 
    def __new__(metacls, name, bases, dct): 
     descriptors = set() 
     for key, obj in dct.items(): 
      if key == "__setattr__": 
       dct[key] = setattr_deco(obj) 
      elif hasattr(obj, "__get__"): 
       descriptors.add(key) 
     dct["_descriptors"] = descriptors 
     return type.__new__(metacls, name, bases, dct) 

# and use MiscSetattr as metaclass for your classes 
4

一種可能的方式:

def __setattr__(self, name, value): 
    print "MyObj.__setattr__ for %s" % name 
    for cls in self.__class__.__mro__ + (self,): 
     if name in cls.__dict__: 
      return object.__setattr__(self, name, value) 
    print 'New name', name, value 
    self.new_names[name] = value 

它檢查名稱類,基礎類或實例已經定義,然後調用object.__setattr__將執行描述符__set__

另一種方式:

def __setattr__(self, name, value): 
    print "MyObj.__setattr__ for %s" % name 
    try: 
     object.__getattribute__(self, name) 
    except AttributeError: 
     print 'New name', name, value 
     self.new_names[name] = value 
    else: 
     object.__setattr__(self, name, value) 

但它會調用描述符的__get__

P.S.

我不確定是否需要檢查所有__mro__成員,因爲MyObj將包含__dict__中的繼承類成員。可能for cls in (self.__class__, self):...就足夠了

+0

裝飾器可以輕鬆實現你第二種方法是行不通的。在我的例子,你被允許在對象上設置新的屬性,這些屬性並沒有存在,這種方法將不允許。 – SpoonMeiser 2012-02-06 15:22:38

+0

@SpoonMeiser,我認爲它就像你的。看看http://ideone.com/HMBcc。也許我誤解了一些東西。 – reclosedev 2012-02-06 15:41:31

+0

如果我做'o .quux = 3',然後'o .__ dict __ ['quux']'將被設置而不是'o.new_names ['quux']' – SpoonMeiser 2012-02-06 15:50:10