实现子类中的抽象属性而不重新定义 __init__ 方法

huangapple go评论80阅读模式
英文:

Implement abstract properties without redefining __init__ in child

问题

我正在学习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 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 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

对于最后一个尝试,如果你在父类的__init__中设置断点并执行dir(self),你会看到其中包含了'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"

以这种方式做有问题吗?

英文:

I am learning the abc module and was wondering if what I want to do is possible.

Essentially, every child I make of the base class should have the SAME exact __init__. The base class will have a few abstract properties that will need to be defined by the child. I would to define those abstract properties without having to rewrite the entire __init__ every time.

Example:

I initially tried something like this

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'

it didn't work.

Then I tried

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

Test

>>> 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

No good either, so then I tried

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

test

>>> 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

For the last one if you set a breakpoint in __init__ of the parent and do a dir(self) you'll see 'prop' in it.

>>> 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']

So I thought that would work.

Edit:

I see I am completely over complicating this. I could have simply did

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"

Is there an issue with doing it this way?

答案1

得分: 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** - 但是self只在类的方法/属性中定义,而不在类体中。

此外,这并不阻止子类修改__init__,任何子类仍然可以修改__init__

英文:

As you've correctly summized, the most straight-forward solution would be to define an abstractproperty and simply let the subclass define it, no need for __init__ at all.

I would go a step further and throw an Exception in the base-class just to make sure there's no stray super() call on it.

import abc

class test(abc.ABC):
    @property
    @abc.abstractmethod
    def prop(self):
        raise NotImplementedException()

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

By the way: Your first example already worked for me. The other two don't work because the second approach tries to find a local variable called prop which doesn't exist and the third approach references self in the class body - but self is only defined in methods/properties of a class, not in the class body.


Also this doesn't prevent __init__ from being overridden in subclasses, any subclass may still modify the __init__.

huangapple
  • 本文由 发表于 2020年1月6日 02:19:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/59602883.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定