python-3.x 定义抽象读写属性以强制getter和setter实现

rvpgvaaj  于 2023-02-20  发布在  Python
关注(0)|答案(1)|浏览(133)

文件

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都是抽象方法,这正是我的目标。
参考:www.example.comhttps://docs.python.org/3.8/library/abc.html#abc.abstractproperty

2jcobegt

2jcobegt1#

从根本上说,问题在于getter和setter只是同一个类属性的一部分,在@propertyx中,你有一个fgetfsetfdel,它们组成了getter,setter和deleter(不一定都设置了),它们可能是抽象的,阻止了init,或者根本不存在。
在你的class D中,你创建了一个新的@property,它完全覆盖了父@property,所以不再有任何抽象的setter来阻止初始化这个类。

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`

相关问题