继承setter,覆盖python抽象类中的getter

时间:2013-10-31 19:56:09

标签: python inheritance properties abc

假设您在基类中有一个属性,其中包含一个setter方法,该方法将在所有子类中使用,但在所有子类中使用不同的getter方法。理想情况下,您只想在基类中编写setter方法的代码一次。此外,您希望将基类设为抽象,因为它没有完整的getter方法。有没有办法做到这一点?

天真,我认为它应该是这样的:

class _mystring(object):

    __metaclass__ = abc.ABCMeta

    @abc.abstractproperty
    def str(self):
        pass

    @str.setter
    def str(self,value):
        self._str = value

class uppercase(_mystring):

    @_mystring.str.getter
    def str(self):
        return self._str.upper()

upper = uppercase()Can't instantiate abstract class uppercase with abstract methods str失败。

如果我将班级@abc.abstractproperty中的_mystring更改为@property,那么upper = uppercase()可以正常工作,但upper = _mystring()也是如此,我不想要。

我错过了什么?

谢谢!

1 个答案:

答案 0 :(得分:3)

我设法围绕abstractproperty制作了一个小包装器来完成它:

class partialAP(abc.abstractproperty):
    def getter(self, func):
        if getattr(func, '__isabstractmethod__', False) or getattr(self.fset, '__isabstractmethod__', False):
            p = partialAP(func, self.fset)
        else:
            p = property(func, self.fset)
        return p

    def setter(self, func):
        if getattr(self.fget, '__isabstractmethod__', False) or getattr(func, '__isabstractmethod__', False):
            p = partialAP(self.fget, func)
        else:
            p = property(self.fset, func)
        return p

然后您的代码只需稍作修改就可以了:

class _mystring(object):

    __metaclass__ = abc.ABCMeta

    @partialAP
    @abc.abstractmethod
    def str(self):
        pass

    @str.setter
    def str(self,value):
        self._str = value

class uppercase(_mystring):

    @_mystring.str.getter
    def str(self):
        return self._str.upper()

然后行为符合要求:

>>> _mystring()
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    _mystring()
TypeError: Can't instantiate abstract class _mystring with abstract methods str
>>> u = uppercase()
>>> u.str = 'a'
>>> u.str
'A'

我的partialAP包装器必须与abc.abstractmethod结合使用。你所做的是使用abstractmethod来装饰你想要抽象的属性(getter或setter)的“片段”,并使用partialAP作为第二个装饰器。 partialAP定义了getter / setter函数,只有当getter和setter都是非抽象的时,才能用普通函数替换该属性。

显然,你必须延长这一点,以便与财产删除者合作。如果你有一个更复杂的继承层次结构,也可能存在无法正确处理的极端情况。

后代的旧解决方案:

class _mystring(object):

    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def _strGet(self):
        pass

    def _strSet(self,value):
        self._str = value

    str = property(_strGet, _strSet)

class uppercase(_mystring):

    def _strGet(self):
        return self._str.upper()
    str = _mystring.str.getter(_strGet)

然后它起作用:

>>> _mystring()
Traceback (most recent call last):
  File "<pyshell#39>", line 1, in <module>
    _mystring()
TypeError: Can't instantiate abstract class _mystring with abstract methods _strGet
>>> u = uppercase()
>>> u.str = 'a'
>>> u.str
'A'

诀窍是你必须不使用方便的装饰器。 abstractproperty装饰器将整个属性标记为抽象。你要做的是创建两个方法,一个是抽象的(对于getter)和一个是常规的(对于setter),然后创建一个组合它们的常规属性。然后,在扩展类时,必须覆盖抽象getter并将其与基类属性显式“混合”(使用_mystring.str.getter)。

您无法使用@_mystring.str.getter的原因是装饰器强制getter和setter与属性本身具有相同的名称。如果你只想将两者中的一个标记为抽象,那么它们必须有不同的名称,否则你不能单独使用它们。

请注意,此解决方案要求子类为其getter提供正确的名称(在本例中为_strGet),以便满足ABC已覆盖抽象方法。