我是 Python 新手,我來自 C ,所以我懷疑我的思維方式被我的先入為主的觀念“玷污”了。我將解釋我正在嘗試做什么以及我面臨的問題,但請注意,下面的代碼是重現我的問題的“人造”小示例。
假設在某些時候我有這種情況,其中 B 只覆寫 A.plot_and_clear() ,因為這就是我需要 B 的全部:
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def clear(self):
print("clear A start")
self.x = 0
self.y = 0
print("clear A end")
def plot(self):
print("plot A start")
print(str(self.x))
print(str(self.y))
print("plot A end")
def plot_and_clear(self):
print("plot & clear A start")
self.plot()
self.clear()
print("plot & clear A end")
class B(A):
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
def plot_and_clear(self):
print("plot & clear B BAD start")
super().plot_and_clear()
print(str(self.z))
self.z = 0
print("plot & clear B BAD end")
def main():
myObject = B(1, 2, 3)
myObject.plot_and_clea()
main()
在這種情況下,輸出正是我所期望的,如下所示:
plot & clear B start
plot & clear A start
plot A start
1
2
plot A end
clear A start
clear A end
plot & clear A end
3
plot & clear B end
后來我意識到我還需要用 B.plot() 覆寫 A.plot(),其余的都保持不變,如下所示:
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def clear(self):
print("clear A start")
self.x = 0
self.y = 0
print("clear A end")
def plot(self):
print("plot A start")
print(str(self.x))
print(str(self.y))
print("plot A end")
def plot_and_clear(self):
print("plot & clear A start")
self.plot()
self.clear()
print("plot & clear A end")
class B(A):
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
def plot(self):
print("plot B start")
super().plot()
print(str(self.z))
print("plot B end")
def plot_and_clear(self):
print("plot & clear B BAD start")
super().plot_and_clear()
print(str(self.z))
self.z = 0
print("plot & clear B BAD end")
def main():
myObject = B(1, 2, 3)
myObject.plot_and_clea()
main()
現在,如果我們運行相同的 main,輸出會發生更改和錯誤,如下所示:
plot & clear B start
plot & clear A start
plot B start
plot A start
1
2
plot A end
3
plot B end
clear A start
clear A end
plot & clear A end
3
plot & clear B end
這是因為現在,B.plot_and_clear() 中的 super().plot_and_clear() 呼叫 B.plot() 而不是 A.plot()。這樣做的結果是,通過簡單地添加一個函式,我打破了 B.plot_and_clear() 以前良好的行為,至少可以說是令人費解的。
我知道這是由于 MRO 在 python 中的作業方式,與 c 相比,它看起來完全顛倒了。現在無論我們是否知道發生這種情況的確切原因,我仍然認為這種行為是不可取的,應該有一種方法來防止它,或者通過選擇一些“安全”的代碼結構或使用其他一些語言結構。
關于我如何“圍繞”或“沿著”語言的這一方面作業的任何想法?
非常感謝。
uj5u.com熱心網友回復:
__init__只應用于初始化現有物件。(盡管物件的創建和對的呼叫__init__通常都發生在對型別本身的呼叫中。)
使用專用的類方法作為替代建構式(例如復制建構式或從另一個物件構造一個物件)。例如,
class Object:
def __init__(self, *, mass=0, **kwargs):
super().__init__(**kwargs)
self.mass_ = mass
@classmethod
def from_object(cls, obj, **kwargs):
return cls(mass=obj.mass_, **kwargs)
class Vehicle(Object):
def __init__(self, *, wheels=4, **kwargs):
super().__init__(**kwargs)
self.wheels_ = wheels
@classmethod
def from_vehicle(cls, vehicle, **kwargs):
return cls(mass=vehicle.mass_, wheels=vehicle.wheels_, **kwargs)
o = Object(mass=100)
v1 = Vehicle.from_object(o)
v2 = Vehicle.from_vehicle(v2)
v3 = Vehicle.from_object(o, wheels=6)
v4 = Vehicle.from_vehicle(v3)
請參閱https://rhettinger.wordpress.com/2011/05/26/super-considered-super/了解我們使用關鍵字引數的原因。
即使它from_object本身不期望任何額外的關鍵字引數,我們也接受cls(可能是Object或任何子類Object)可能期望的關鍵字引數。
還要注意,它Vehicle本身不必定義from_object; Vehicle.from_object將改為用于Object.from_object創建車輛。這聽起來可能很奇怪,但 的作業不一定Object.from_object是創建一個,而是知道如何“解包”一個實體以創建一個.ObjectObjectcls
uj5u.com熱心網友回復:
由于上面評論中的冗長討論,我想我已經掌握了在 python 中所有函式都是虛擬的概念,因此當所有基類方法都是虛擬的時,所描述的行為也正是人們在 C 中所期望的。
然而,可以選擇將基類函式宣告為非虛擬函式,這樣當從另一個基類方法中呼叫基類方法時,無論子類做什么,都可以準確控制它的行為方式與他們的覆寫。
到目前為止,我找到了兩種方法可以在一定程度上重現該功能或至少部分控制基類的行為:
方法 1):當需要非虛擬行為時,在其他基類方法中顯式呼叫基類方法:
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def clear(self):
print("clear A start")
self.x = 0
self.y = 0
print("clear A end")
def plot(self):
print("plot A start")
print(str(self.x))
print(str(self.y))
print("plot A end")
def plot_and_clear(self):
print("plot & clear A start")
A.plot(self)
A.clear(self)
print("plot & clear A end")
class B(A):
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
def plot(self):
print("plot B start")
super().plot()
print(str(self.z))
print("plot B end")
def plot_and_clear(self):
print("plot & clear B BAD start")
super().plot_and_clear()
print(str(self.z))
self.z = 0
print("plot & clear B BAD end")
def main():
myObject = B(1, 2, 3)
myObject.plot_and_clea()
main()
# Output as desired despite the presence of overrides:
# plot & clear B start
# plot & clear A start
# plot A start
# 1
# 2
# plot A end
# clear A start
# clear A end
# plot & clear A end
# 3
# plot & clear B end
方法 2) Name-Mangle 那些需要非虛擬的函式,并且只在內部使用 name-mangled 版本:
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def clear(self):
print("clear A start")
self.x = 0
self.y = 0
print("clear A end")
__clear = clear
def plot(self):
print("plot A start")
print(str(self.x))
print(str(self.y))
print("plot A end")
__plot = plot
def plot_and_clear(self):
print("plot & clear A start")
self.__plot()
self.__clear()
print("plot & clear A end")
class B(A):
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z
def plot(self):
print("plot B start")
super().plot()
print(str(self.z))
print("plot B end")
def plot_and_clear(self):
print("plot & clear B BAD start")
super().plot_and_clear()
print(str(self.z))
self.z = 0
print("plot & clear B BAD end")
def main():
myObject = B(1, 2, 3)
myObject.plot_and_clea()
main()
# Output as desired despite the presence of overrides:
# plot & clear B start
# plot & clear A start
# plot A start
# 1
# 2
# plot A end
# clear A start
# clear A end
# plot & clear A end
# 3
# plot & clear B end
我希望這可以幫助像我一樣在從靜態語言轉換到動態語言時遇到困難的任何人,盡管可能在某些時候我們應該更好地接受該語言的核心哲學,而不是試圖強行使其適應我們的舊方式:)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/454390.html
下一篇:分配列舉值c#
