我有一个继承场景,看起来像这样:
import abc
class A(metaclass=abc.ABCMeta):
def __init__(self, x: int | None = None) -> None:
self.x = x
@abc.abstractmethod
def foo(self, x: int | None = None) -> None:
pass
class B(A):
def foo(self, x: int | None = None) -> None:
x = x or self.x
if x is None:
raise ValueError("x must be defined")
print(x)
class C(B):
... # defines a bunch of other stuff
这个想法是,它将可能为用户(其他开发人员)以两种不同的方式使用我的类。要么他们直接与B
交互(或A
的另一个等价子类),然后它们必须将某些值传递给它的方法(在本例中,它们需要将x
传递给B.foo
方法。它们可以改为子类化B
(类似于本例中的C
),然后直接在init中设置x
,然后随后不需要将x
传递到C.foo
。
最后,这两种用法都应该是有效的:
b = B()
b.foo(x=1)
c = C(x=1)
c.foo()
现在的问题是,我希望避免在A
的不同方法和不同子类中重复该子句:
x = x or self.x
if x is None:
ValueError("x must be defined")
是否有任何方法可以在A
中执行一些操作,这些操作本质上将运行此检查,并且如果没有为x
指定值,则将沿着x
的值有效地传递给foo
方法?
我实际上希望将self.x
设置为x
的默认值,但如果可以避免的话,我不希望重写if x is None
检查。
我想到的是,如果我能以某种方式自动注册一个装饰器到我的子类的方法上,但我不认为这是可能的?
1条答案
按热度按时间bxgwgixi1#
您要覆盖的不是
foo
,而是 *foo
* 使用的方法。让我们从更新
A
开始。注意,
foo_work
* 必须 * 接收一个x
;它不会接受None
作为参数,但这没关系,因为最终用户永远不会直接调用foo_work
:它们将只调用foo
,foo
或者找到foo_work
的有效参数,或者引发异常。这可能看起来有点尴尬:你可以看到
A
的实现细节作为其公共接口的一部分被公开,这是因为foo
实际上是两个方法被合并成一个:1.将值作为参数的函数
1.从自身内部状态取值的对象
可以说,您应该将这两者分开:
现在,这两个方法都是公共接口的一部分,实现
foo
需要子类,foo_with_default
本质上是最终的,不应该被覆盖(尽管abc
没有提供一种方法来标记它),代价是最终用户必须知道要使用哪个函数。