【问题标题】:Define abstract read-write property to force getter and setter implementation定义抽象读写属性以强制执行 getter 和 setter
【发布时间】:2020-03-27 16:24:56
【问题描述】:

文档

Python 文档(3.8 版):

上面的例子定义了一个只读属性;您还可以通过将一个或多个底层方法适当地标记为抽象来定义读写抽象属性:

class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

如果只有一些组件是抽象的,那么只有那些组件需要更新以在子类中创建具体属性:

我的代码

from abc import ABC, abstractmethod
class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

class D(C):
    @property
    def x(self):
        pass

d = D()

预期行为:实例化失败,因为有未定义的抽象方法

实际行为:实例化没有错误

问题

为什么没有实例化失败?如果派生类中没有实现 setter,我该如何编写它,所以它确实无法实例化。文档似乎表明这应该是abstractmethod 的一个涵盖用例。文档还提供了一个示例,其中 getter 和 setter 都是抽象方法,这正是我的目标。

参考: https://docs.python.org/3.8/library/abc.html#abc.abstractproperty

【问题讨论】:

    标签: python python-3.x oop abstract


    【解决方案1】:

    从根本上说,getter 和 setter 只是同一个类属性的一部分。在@propertyx 中,您有一个fgetfsetfdel,它们组成了getter、setter 和deleter(不一定全部设置)。这些可能是抽象的并阻止初始化,或者根本不存在。

    在您的class D 中,您创建了一个新的@property,它完全覆盖了父@property,因此不再有任何抽象设置器来阻止初始化类。

    from abc import ABC, abstractmethod
    class C(ABC):
        @property
        def x(self):
            pass
    
        @x.setter
        @abstractmethod
        def x(self, val):
            pass
    
    class D(C):
        @property
        def x(self):
            pass
    
    def examine_property(p):
        print('get', p.fget, getattr(p.fget, '__isabstractmethod__', False) if p.fget is not None else None)
        print('set', p.fset, getattr(p.fset, '__isabstractmethod__', False) if p.fset is not None else None)
        print('del', p.fdel, getattr(p.fdel, '__isabstractmethod__', False) if p.fdel is not None else None)
    
    print("C.x:")
    examine_property(C.x)
    print("D.x:")
    examine_property(D.x)
    

    输出:

    C.x:
    get <function C.x at 0x101ac4550> False
    set <function C.x at 0x101ac45e0> True
    del None None
    D.x:
    get <function D.x at 0x101ac4670> False
    set None None
    del None None
    

    所以,如果你想覆盖 getter,你需要非常具体地使用 @C.x.getter

    class D(C):
        @C.x.getter
        def x(self):
            print("non-abstract getter")
    

    输出:

    C.x:
    get <function C.x at 0x1022fb550> False
    set <function C.x at 0x1022fb5e0> True
    del None None
    D.x:
    get <function D.x at 0x1022fb670> False
    set <function C.x at 0x1022fb5e0> True
    del None None
    

    这样你就不会覆盖整个property,只覆盖它的特定功能。

    我不会混合创建新的 properties 和覆盖父母,我在测试中看到了这种挑剔的行为:

    class D(C):
        @property
        def x(self):
            pass
        @C.x.setter
        def x(self, val):
            pass
    print("D.x:")
    examine_property(D.x)
    

    输出:

    D.x:
    get <function C.x at 0x10cdd74c0> False
    set <function D.x at 0x10cdd7670> False
    del None None
    # Our new override property is gone
    

    重新排序的类定义:

    class D(C):
        @C.x.setter
        def x(self, val):
            pass
        @property
        def x(self):
            pass
    

    输出:

    D.x:
    get <function D.x at 0x105048670> False
    set None None
    del None None
    # We entirely lost the `setter`
    

    【讨论】:

      猜你喜欢
      • 2012-01-31
      • 1970-01-01
      • 2023-03-04
      • 2019-10-13
      • 1970-01-01
      • 2012-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多