【问题标题】:Inheriting setter, overwriting getter in python abstract class继承setter,覆盖python抽象类中的getter
【发布时间】:2013-11-11 23:44:54
【问题描述】:

假设您在基类中有一个属性,该属性具有单个 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

如果我将_mystring 类中的@abc.abstractproperty 更改为@property,则upper = uppercase() 有效,但upper = _mystring() 也有效,这是我不想要的。

我错过了什么?

谢谢!

【问题讨论】:

    标签: python inheritance properties abc


    【解决方案1】:

    我设法在 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。

    【讨论】:

    • 这太糟糕了,因为使用装饰器更像是pythonic。在这种情况下,抽象类对基类唯一隐藏的是 setter 函数的名称,因为您必须知道正确的 getter 名称并且必须进行显式混合。
    • @Seanslice:查看我编辑的答案。我能够制作出 abstractproperty 的修改版本,它允许您独立地将 getter/setter 标记为抽象并使用装饰器。
    猜你喜欢
    • 2011-03-11
    • 2011-03-21
    • 2015-02-07
    • 2023-03-04
    • 1970-01-01
    • 2014-11-12
    • 2020-08-16
    相关资源
    最近更新 更多