2016-11-14 57 views
0

我已經實現了一個類,它可以區分一組固定的實例屬性(我們稱之爲元屬性)和任意一組其他實例屬性。Python深度定製__getattr__和__setattr__

它定製__getattr____setattr__

class MyClass(object): 

    def __init__(self, meta1, meta2, **other_attr): 
     super(MyClass, self).__setattr__('meta1', meta1) 
     super(MyClass, self).__setattr__('meta2', meta2) 
     super(MyClass, self).__setattr__('params', {}) 

     self.params = {key: other_attr[key] for key in other_attr} 


    # this is called when default lookup finds nothing 
    def __getattr__(self, key): 
     print('__getattr__({})'.format(key)) 
     try: 
      return self.params[key] 
     except KeyError: 
      raise AttributeError(key) 


    # this is called always 
    def __setattr__(self, key, value): 
     print('__setattr__({}, {})'.format(key, value)) 
     if key in self.__dict__: 
      super(MyClass, self).__setattr__(key, value) 
     else: 
      self.params[key] = value 

這工作得很好,所有的元屬性直接進入實例的__dict__,而所有其他屬性進入params詞典:

obj1 = MyClass(meta1 = 'foo', meta2 = 'bar', x=1, y=2, z=3) 
obj1.w = 4 
print(obj1.__dict__) 

輸出:

__setattr__(params, {'y': 2, 'x': 1, 'z': 3}) 
__setattr__(w, 4) 
{'meta1': 'foo', 'meta2': 'bar', 'params': {'y': 2, 'x': 1, 'z': 3, 'w': 4}} 

除了當我嘗試deepcopy我的目標,但它確實有些奇怪:

import copy 
obj1 = MyClass(meta1='foo', meta2='bar', x=1, y=2, z=3) 
obj2 = copy.deepcopy(obj1) 

輸出:

__setattr__(params, {'y': 2, 'x': 1, 'z': 3}) 
__getattr__(__deepcopy__) 
__getattr__(__getnewargs__) 
__getattr__(__getstate__) 
__getattr__(__setstate__) 
__getattr__(params) 
__getattr__(params) 
__getattr__(params) 
__getattr__(params) 
__getattr__(params) 
__getattr__(params) 
__getattr__(params) 
__getattr__(params) 
... 
and then it calls __getattr__ about a hundred more times 

在它創建一個副本結束,但爲什麼它打了很多電話給__getattr__

回答

2

這是造成遞歸查詢(記住obj2通過__init__將不會被初始化)

return self.params[key] 

而應該做到這一點

return super().__getattribute__('params')[key] 
+0

謝謝,這工作。但爲什麼它不會導致*無限*遞歸,但在某個點停止? – spiderface

+0

@spiderface,我得到'RuntimeError:調用Python對象時超出最大遞歸深度。也許你嘗試了一個具有稍微不同的異常處理程序的版本 –

+0

這很奇怪。我還發現,我不必從'__getattribute__'返回值,只需調用它然後返回'return self.params [key]'就足夠了。 – spiderface