【问题标题】:Implement abstract properties without redefining __init__ in child实现抽象属性而不在 child 中重新定义 __init__
【发布时间】:2020-04-23 10:51:51
【问题描述】:

我正在学习abc 模块并且想知道我想做的事情是否可行。

基本上,我为基类创建的每个孩子都应该拥有完全相同的__init__。基类将具有一些需要由子类定义的抽象属性。我会定义这些抽象属性,而不必每次都重写整个__init__

例子:

我最初尝试过这样的事情

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
       pass

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):

    @property
    def prop(self):
        return "THIS WORKS"
>>> from abc_tests import prop_ex
>>> blah = prop_ex()
>>> blah.prop
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'prop_ex' object has no attribute 'prop'

没用。

然后我尝试了

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
        self.prop = prop

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):
    prop = "THIS WORKS"

    @property
    def prop(self):
        return self._prop

测试

>>> from abc_tests import prop_ex
>>> blah = prop_ex()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "abc_tests.py", line 13, in __init__
    self.prop = prop
NameError: name 'prop' is not defined

也不好,所以我尝试了

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
        pass

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):
    self.prop = "THIS WORKS"

    @property
    def prop(self):
        return self._prop

测试

>>> from dunder_tests import prop_ex
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "abc_tests.py", line 39, in <module>
    class prop_ex(test):
  File "abc_tests.py", line 40, in prop_ex
    self.prop = "THIS WORKS"
NameError: name 'self' is not defined

对于最后一个,如果您在父级的__init__ 中设置断点并执行dir(self),您将在其中看到'prop'

>>> blah = prop_ex()
> abc_tests.py(14)__init__()
-> self.prop = prop
(Pdb) dir(self)
['__abstractmethods__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', 'prop']

所以我认为这会奏效。

编辑:

我知道我完全把这个复杂化了。我本来可以做的

from abc import ABC,abstractmethod

class test(ABC):

    def __init__(self):
        pass

    @property
    @abstractmethod
    def prop(self):
        pass

class prop_ex(test):

    prop = "THIS WORKS"

这样做有问题吗?

【问题讨论】:

    标签: python python-3.x class abstract-class abc


    【解决方案1】:

    正如您正确总结的那样,最直接的解决方案是定义一个 abstractproperty 并简单地让子类定义它,根本不需要 __init__

    我会更进一步,在基类中抛出一个异常,以确保没有杂散的super() 调用。

    import abc
    
    class test(abc.ABC):
        @property
        @abc.abstractmethod
        def prop(self):
            raise NotImplementedException()
    
    class prop_ex(test):
        prop = "THIS WORKS"
    

    顺便说一句:你的第一个例子已经对我有用。其他两个不起作用,因为第二种方法试图找到一个名为 prop局部变量,它不存在,而第三种方法在类主体中引用 self strong> - 但self 仅在类的方法/属性中定义,而不在类主体中。


    这也不会阻止__init__ 在子类中被覆盖,任何子类仍然可以修改__init__

    【讨论】:

    • 文档说 abstractproperty 自 3.3 起已弃用。建议使用@property@abstractmethod
    • @Melendowski 谢谢。我完全忽略了这一点。我从答案中删除了那段。
    • 我一定是用第一种方法或其他方法制作了一个 booboo,因为它现在对我有用,实际上它是首选,因为当前的解决方案不保护属性为只读,这是我想要的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    • 2022-11-12
    • 2018-10-21
    • 1970-01-01
    • 1970-01-01
    • 2010-11-30
    • 1970-01-01
    相关资源
    最近更新 更多