假设您在基类中具有一个属性,该属性具有将在所有子类中使用的单个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()可以,我不需要。

我想念什么?

谢谢!

最佳答案

我设法在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都具有与属性本身相同的名称。如果要仅将两者之一标记为抽象,则它们必须具有不同的名称,否则就不能分开使用它们。

请注意,此解决方案要求子类为它们的 setter/getter 赋予正确的名称(在这种情况下为_strGet),以便使ABC覆盖它们的抽象方法。

关于python - 继承setter,覆盖python抽象类中的getter,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19715507/

10-13 06:52