在具有属性的类中重写__getattr__和__setattr__方法

时间:2017-02-07 16:09:04

标签: python python-3.x

所以我试图实现一个包含字典值和延迟评估属性的混合类。我希望在语法上能够通过__getitem____getattr__访问类中的项目,并以相同的方式设置它们的值,但在存在时使用属性when和setter。

我设想如何实现这个的一些示例代码:

class C(object):    
    def __init__(self):
        self.data = {'akey':'avalue'}
        self.att1 = '1st attribute'
        self._prop1 = None

    def __getitem__(self,key):
        try:
            self.__dict__[key]
        except KeyError:
            return self.data[key]

    def __setitem__(self,key,val):
        self.data[key] = val

    def __getattr__(self,name):
        try:
            self.__dict__[name]
        except KeyError:
            return self.data[name]

    def __setattr__(self,name,val):
        try:
            self.__dict__[name] = val
        except KeyError:
            self.data[name] = val

    @property
    def prop1(self):
        #do expensive stuff here in real class
        return self._prop1
    @prop1.setter
    def prop1(self,newval):
        #check/change value for prop1
        self._prop1 = str(newval).upper()

属性访问适用于属性,但不适用于__getitem__方法。

In [35]: c = C()
In [36]: c.prop1
Out[36]: 'Prop1 Calc Value'

In [37]: c['prop1']
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
C:\Users\someuser\somepath\foo.py in __getitem__(self, key)
      8         try:
----> 9             self.__dict__[key]
     10         except KeyError:

KeyError: 'prop1'

During handling of the above exception, another exception occurred:

KeyError                                  Traceback (most recent call last)
C:\Users\someuser\somepath\foo.py in <module>()
----> 1 c['prop1']

C:\Users\someuser\somepath\foo.py in __getitem__(self, key)
      9             self.__dict__[key]
     10         except KeyError:
---> 11             return self.data[key]
     12
     13     def __setitem__(self,key,val):

KeyError: 'prop1'

我认为这是因为我不完全理解@property装饰器在课堂上是如何工作的,但如果有一种惯用的方法可以解决这个问题,我很乐意听到它!我知道它真的是语法糖,但是对于类具有灵活的属性访问并且仍然能够支持不是有效的python变量名的键会很好。 pandas.DataFrame / collections.namedtuple api是这方面的灵感来源。

修改

工作解决方案:

Thansk向@Jim Fasarakis-Hilliard指出我正确的方向,以下代码在我的系统上工作(python 3.5):

class C(object):    
    def __init__(self):
        self.data = {'akey':'avalue'}
        self.att1 = '1st attribute'
        self._prop1 = None

    def __getitem__(self,key):
        try:
            return self.__dict__[key]
        except KeyError:
            try: 
                return getattr(type(self), key).__get__(self)
            except AttributeError:
                return self.data[key]
    __getattr__ = __getitem__

    def __setitem__(self,key,value):
        #note order is look for property 1st so that any 
        #setter methods get used insead of attribute access
        try:
            getattr(type(self), key).__set__(self,value)
        except AttributeError:
            try: 
                self.__dict__[key] = value
            except KeyError:
                self.data[key] = value

    __setattr__ = __setitem__

    @property
    def prop1(self):
        #do expensive stuff here in real class
        if not self._prop1:
            self._prop1='Prop1 Calc Value'
        return self._prop1
    @prop1.setter
    def prop1(self,newval):
        #check/change value for prop1
        print('using setter')
        self._prop1 = str(newval).upper()

In [62]: c = C()

In [63]: c['prop1']
Out[63]: 'Prop1 Calc Value'

In [64]: c['prop1'] = 'new value'
using setter

In [65]: c['prop1']
Out[65]: 'NEW VALUE'

In [66]: c.akey
Out[66]: 'avalue'

In [67]: c.att1
Out[67]: '1st attribute'

1 个答案:

答案 0 :(得分:2)

我猜你希望你的实例能够通过下标符号访问描述符;我不知道你为什么要这么做,但属性存在于类名称空间中,所以你需要看看那里。

试着从类的dict中获取描述符并调用它:

 def __getitem__(self,key):
    try:
        return self.__dict__[key]
    except KeyError:
        try: 
            return getattr(type(self), key).__get__(self)
        except KeyError:
            return self.data[key]

然后(我猜)你可以做你想做的事:

>>> c = C()
>>> c['prop1']
'prop1'