假设我有一个继承了类T
的类TT
,并扩展了它的一个方法的行为,如下所示:
class T:
def __init__(self, p, b):
self.p = p
self.b = b
self.fun()
def fun(self):
a = self.p + self.b
print(a)
class TT(T):
def __init__(self, p, b):
super().__init__(p, b)
def fun(self):
super().fun()
c = self.p*self.b
print(c)
我想要的是TT.fun
依赖于本例中T.fun
,a
中定义的变量。想到的两个明显的事情是使T.fun
返回a
,或者使a
成为实例变量,但我觉得这两个都不令人满意。是否有一种方法TT.fun
共享它扩展的方法的范围?
编辑谢谢你的答案。
为了扩展我的用例,我的派生类针对数据集运行某种向量优化。父类在这里建议为不同的优化提供一个公共的、一致的接口。在一个类的函数被优化,一些步骤(取决于向量的当前状态)由所有子类共享,这就是为什么我认为我会把它放在一个父方法。正如拉格贝尔所言,它们已经在相当于"私人"的环境中运行了。compute_a()
方法;我想到的是调用这个方法将结果带到父类的作用域,这样我就不必在各个子类中做这件事了。事实证明,考虑到我正在处理的子类的数量很少,在这里复制几行代码可能是最简单的事情。
关于Nathaniel Ford的建议,这就是我觉得我最初的解决方案不令人满意的原因:
- 我不需要这个值一旦优化已经运行,它在每一个优化步骤的变化,这就是为什么我不认为它是有意义的保存为实例变量。
- 返回该值将导致扩展方法的行为与其父方法不同(在签名意义上)。
- 我不希望任何东西在全局作用域中逗留。
正如你指出的,我的想法不是封装友好的;偶尔有这样的提醒是好的!
没有直截了当的方法。(我甚至不知道是否有是的一个hack的方式)。
你的提议违反了封装原则。一个类隐藏了它的基本的肮脏的内部,只公开了一个整洁的接口和承诺的行为。
继承不是违背这一原则的机制。
在您的具体示例中,问题来自T
的糟糕界面设计。如果T
有一个方法compute_a()
,它会返回self.p + self.b
,那么在你继承的类中,你当然可以调用self.compute_a()
。
但只有当a
不仅仅是一个内部实现细节时才这样做!
简短的回答是"不",特别是因为一个子类有一个与父类同名的函数并不意味着这些函数共享任何东西。当你调用super().fun()
时,你是在父类上调用fun()
,但这与在一个完全不相关的类上调用fun()
是一样的——除非父类共享它的状态(实例变量)。
因此,子类实例访问父类实例函数中发生的计算的唯一方法是通过正常模式:
- 将结果保存到实例的状态(实例变量)。
- 返回值,使
super().fun()
变为a = super().fun()
- 保存到其他更全局范围的值(不推荐)。
当你说没有方法是令人满意的,你应该再深入一点了解情况并检查原因。还有一些替代方法,例如私有方法,可以以干净的方式解决看似混乱的情况。也就是说,您的示例并没有演示为什么fun()
甚至被覆盖,或者您希望通过共享作用域来完成什么。
您可以简单地从父类中返回fun()
的值,然后将返回值分配给子方法中的变量。这当然有效,但我不认为这是一个好的设计:
class T:
def __init__(self, p, b):
self.p = p
self.b = b
self.fun()
def fun(self):
a = self.p + self.b
return a
class TT(T):
def __init__(self, p, b):
super().__init__(p, b)
def fun(self):
a = super().fun()
c = self.p * self.b * a
return c
t = T(2, 3)
tt = TT(2, 3)
print(t.fun()) # >> 5
print(tt.fun()) # >> 30