2013-04-17 39 views
2

我有一個類LabelMapper(一個boost::python類),它實現字典協議。我想有一個代理類,它將使用屬性來訪問該dicionary。我已經看到很多帖子,覆蓋__setitem____getitem__,但我似乎無法做到正確。代理類訪問其他類的項目作爲屬性(__getitem__無限遞歸)

簡易方法(下面)導致無限遞歸由於self.mapper調用LabelMapperProxy.__getattr__,這又需要self.mapper等。

class LabelMapper(object): 
    def __init__(self): self.map={} 
    def __getitem__(self,key): return self.map[key] 
    def __setitem__(self,key,val): self.map[key]=val 
    def __delitem__(self,key): del self.map[key] 

class LabelMapperProxy(object): 
    def __init__(self,mapper): self.mapper=mapper 
    def __getattr__(self,key): return self.mapper[key] 
    def __setattr__(self,key,val): self.mapper[key]=val 
    def __delattr__(self,key): del self.mapper[key] 

lm=LabelMapper() 
lm['foo']=123 

# construct the proxy 
lmp=LabelMapperProxy(mapper=lm) 
print lmp.foo     # !!! recursion 
lmp.bar=456 
print lmp.bar,lm['bar'] 

什麼是解決方案?也許有在標準庫中預煮的代理嗎?

回答

6

您正在嘗試設置一個新的屬性您Proxy實例:

class LabelMapperProxy(object): 
    def __init__(self, mapper): self.mapper = mapper 

這會觸發__setattr__,它試圖訪問不存在self.mapper屬性,所以__getattr__提供諮詢(被稱爲爲所有缺失的屬性)。而__getattr__試圖訪問self.mapper ....

的解決方法是直接在self.__dict__設置mapper

class LabelMapperProxy(object): 
    def __init__(self, mapper): self.__dict__['mapper'] = mapper 

另外,使用原來的基類__setattr__只是爲mapper屬性:

class LabelMapperProxy(object): 
    def __init__(self, mapper): self.mapper = mapper 

    def __setattr__(self, key, val): 
     if key == 'mapper': 
      return super(LabelMapperProxy, self).__setattr__(key, val) 
     self.mapper[key] = val 
+0

只是我的2c:我會將'mapper'重命名爲'__mapper',這樣如果有人想使用'mapper'作爲屬性名稱,它將保持一致。 – bereal

+0

@ bereal:當然,但這並不能解決這裏的問題。 :-) –

+0

太棒了!在'__getattr__'中查找'self.mapper'時,如何讓'__getattr__'不進入循環?那是'__getattribute__'和'__getattr__'的區別嗎? – eudoxos

1

這是陷阱:

class LabelMapperProxy(object): 
    def __init__(self, mapper): 
     # This will not assign this object's attribute 
     # since __setattr__ is overriden. 
     # Instead, it will do self.mapper['mapper'] = mapper 
     self.mapper=mapper 

    def __getattr__(self, key): 
     # And this won't find `self.mapper` and resort to __getattr__ 
     # (which is itself) 
     return self.mapper[key] 

    def __setattr__(self, key, val): 
     self.mapper[key]=val 

    def __delattr__(self, key): 
     del self.mapper[key]