Python仅使用装饰器覆盖abstractmethod属性的setter和getter

时间:2018-08-11 09:23:21

标签: python python-3.x inheritance abstract

我正在尝试创建一个抽象类,该类将强制实现同时实现getter和setter。我正在按照https://docs.python.org/3.4/library/abc.html#abc.abstractproperty中的描述进行操作,但是我一直在获取

Traceback (most recent call last):
  File "test.py", line 30, in <module>
    test = B('bar')
TypeError: Can't instantiate abstract class B with abstract methods foo

即使我在B中同时实现了foo的getter和setter方法。

这是我的代码:

from abc import ABC, abstractmethod

class A(ABC):
    @property
    @abstractmethod
    def foo(self):
        pass

    @foo.setter
    @abstractmethod
    def foo(self, val):
        pass

    def do_stuff(self):
        print(self.foo)

class B(A):
    def __init__(self, val):
        self._foo = val

    @property
    def foo(self):
        return self._foo

    @A.foo.setter
    def foo(self, val):
        self._foo = val

if __name__ == '__main__':
    test = B('bar')
    test.do_stuff()
    test.foo = 'barr'
    test.do_stuff()

我知道有一种方法可以通过定义属性而不使用装饰器来做到这一点,

from abc import ABC, abstractmethod

class A(ABC):
    @abstractmethod
    def _get_foo(self):
        pass

    @abstractmethod
    def _set_foo(self, val):
        pass

    def do_stuff(self):
        print(self.foo)

    foo = property(_get_foo, _set_foo)

class B(A):
    def __init__(self, val):
        self._foo = val

    def _get_foo(self):
        return self._foo

    def _set_foo(self, val):
        self._foo = val

    foo = property(_get_foo, _set_foo)

if __name__ == '__main__':
    test = B('bar')
    test.do_stuff()
    test.foo = 'barr'
    test.do_stuff()

这确实可行,但肯定有一种方法可以只用装饰器来做到这一点,不是吗?

我正在运行python 3.4。

2 个答案:

答案 0 :(得分:0)

您的代码引发异常,因为您在A.foo.setter方法上使用了B.foo装饰器,因此B.foo方法实际上并没有实现抽象方法A.foo与相同的签名。

从装饰器中删除A.规范,您的代码将起作用:

@foo.setter
def foo(self, val):
    self._foo = val

使用此修复程序,您会发现类A确实强制子类实现foo的getter和setter方法(您看到的异常实际上是由于您没有实施设置程序)。

答案 1 :(得分:0)

根据此issue,您需要遍历层次结构中仅实现getter的其他类。然后,设置员将继续使用具体的课程:

from abc import ABC, abstractmethod

class A(ABC):

    @property
    @abstractmethod
    def foo(self):
        pass

    @foo.setter
    @abstractmethod
    def foo(self, val):
        pass

    def do_stuff(self):
        print(self.foo)

class B(A):

    _foo = None

    @A.foo.getter
    def foo(self):
        return self._foo

class C(B):

    def __init__(self, val):
        self._foo = val

    @B.foo.setter
    def foo(self, val):
        self._foo = val

if __name__ == '__main__':
    test = C('bar')
    test.do_stuff()
    test.foo = 'barr'
    test.do_stuff()

如果您删除B中的getter或C中的setter,您将获得所需的TypeError异常。

但是,此解决方案迫使您将getter和setter的定义放在两个不同的类中,这在实际应用中可能是不切实际的。