[英]Python, Overriding an inherited class method
我有兩個類, Field
和Background
。 他們看起來有點像這樣:
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
def buildField( self ):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
def buildField( self, c ):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background( a, b, c )
此錯誤指向Field的buildField()
:
"TypeError: buildField() takes exactly 2 arguments (1 given)."
我期望首先調用Background init ()。 要將“a,b”傳遞給Fields init (),要將字段分配給a和b,然后將其中包含三個0的列表分配給字段。 然后為后台的init ()繼續,然后調用它自己的buildField()並使用包含c的列表覆蓋self.field。
我似乎並不完全理解super(),但是在查看網絡上和周圍的類似繼承問題后,我無法找到解決問題的方法。
我期望像c ++這樣的行為,其中一個類可以覆蓋一個繼承的方法。 我怎樣才能實現這一點或類似的東西。
我發現與此相關的大多數問題都是使用雙下划線的人。 我使用super繼承的經驗是使用繼承的類init ()將不同的變量傳遞給超類。 什么都不涉及覆蓋任何東西。
我期望調用Background init()。 要將“a,b”傳遞給Fields init(),將Field傳遞給a和b
到現在為止還挺好。
然后將一個包含三個0的列表分配給字段。
啊。 這是我們得到錯誤的地方。
self.field = self.buildField()
即使這一行發生在Field.__init__
, self
也是Background
一個實例。 所以self.buildField
找到了Background
的buildField
方法,而不是Field
的。
由於Background.buildField
需要2個參數而不是1個,
self.field = self.buildField()
引發錯誤。
那么我們如何告訴Python調用Field
的buildField
方法而不是Background
?
名稱修改 (用雙下划線命名屬性)的目的是解決這個確切的問題。
class Field(object):
def __init__(self, a, b):
self.a = a
self.b = b
self.field = self.__buildField()
def __buildField(self):
field = [0,0,0]
return field
class Background(Field):
def __init__(self, a, b, c):
super(Background, self).__init__(a, b)
self.field = self.__buildField(c)
def __buildField(self, c):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background(a, b, c)
方法名__buildField
是“錯位”對_Field__buildField
內部Field
所以內部Field.__init__
,
self.field = self.__buildField()
調用self._Field__buildField()
,這是Field
的__buildField
方法。 同樣地,
self.field = self.__buildField(c)
在Background.__init__
調用Background
的__buildField
方法。
從C ++的角度來看,這里可能存在兩個誤解。
首先,具有相同名稱和不同簽名的方法不會像在C ++中那樣使其重載。 如果您的一個Background對象嘗試調用不帶參數的buildField,則不會調用Field的原始版本 - 它已被完全隱藏。
第二個問題是,如果超類中定義的方法調用buildField,則將調用子類版本。 在python中, 所有方法都是動態綁定的,就像C ++ virtual
方法一樣。
Field的__init__
期望處理一個沒有參數的buildField方法的對象。 您將該方法與具有buildField方法的對象一起使用一個參數。
super
的東西是它不會改變對象的類型,所以你不應該改變超類'方法可能調用的任何方法的簽名。
我期望調用Background init()
實際上Background init()
被調用了..
但是看看你的Background類..
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.field = self.buildField( c )
所以, __init__
的第一個語句是調用super class(Field)
init方法..並將self
作為參數傳遞。現在這個self
實際上是Background class
的引用。
現在你的Field類: -
class Field( object ):
def __init__( self, a, b ):
print self.__class__ // Prints `<class '__main__.Background'>`
self.a = a
self.b = b
self.field = self.buildField()
你的buildField()
方法實際上正在調用Background類中的那個。這是因為,這里的self
是Background
類的實例(嘗試在Field class
__init__
方法中打印self.__class__
)。當你在調用時傳遞它來自Background
類的__init__
方法..
這就是你收到錯誤的原因..
錯誤“TypeError:buildField()只需要2個參數(給定1個)。
因為你沒有傳遞任何值..所以,只有傳遞的值才是隱含的self
。
super(Background, self).__init__( a, b )
將調用:
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
在Field
。 然而, self
這里指的是background
情況,並self.buildField()
實際上是調用buildField()
的Background
,這就是為什么你得到這個錯誤。
似乎您的代碼應該更好地編寫為:
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = Field.buildField()
@classmethod
def buildField(cls):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__(a, b)
self.field = Background.buildField(c)
@classmethod
def buildField(cls,c):
field = [c]
return field
a, b, c = 0, 1, 2
background = Background( a, b, c )
如果您不能允許基礎構造函數完成,那么它表示設計存在缺陷。
因此,如果必須在構造函數中調用這些方法,則最好使用classmethod
decorator或staticmethod
將buildField()
分離為屬於該類。
但是,如果基類構造函數未從內部調用任何實例方法,則可以安全地覆蓋此基類的任何方法。
談論Overriding
,但聽起來像chaining constructors or (methods)
而且它聽起來像覆蓋屬性:
讓我解釋:
名為field的屬性將初始化為[0,0,0]
。 @property
裝飾器看起來更合適。
然后, Background
類覆蓋此屬性。
我不知道你的業務邏輯,但有時通過超級的__init__
方法給了我更多的控制權:
#!/usr/bin/env python
class Field( object ):
def __init__( self, a, b ):
self.a = a
self.b = b
self.field = self.buildField()
def buildField( self ):
field = [0,0,0]
return field
class Background( Field ):
def __init__( self, a, b, c ):
# super(Background, self).__init__( a, b )
# Unfortunately you should repeat or move initializing a and b
# properties here
self.a = a
self.b = b
self.field = self.buildField( c )
def buildField( self, c ):
# You can access super class methods
assert super(Background, self).buildField() == [0,0,0]
field = [c]
return field
a, b, c = 0, 1, 2
bg = Background(a,b,c)
assert bg.field == [2]
有更清晰的語法。
#!/usr/bin/env python
class Field( object ):
@property
def field(self):
return [0,0,0]
def __init__( self, a, b ):
self.a = a
self.b = b
class Background( Field ):
def __init__( self, a, b, c ):
super(Background, self).__init__( a, b )
self.c = c
assert (self.a, self.b, self.c) == (0,1,2) # We assigned a and b in
# super class's __init__ method
assert super(Background, self).field == [0,0,0]
assert self.field == [2]
@property
def field(self):
return [self.c]
a, b, c = 0, 1, 2
background = Background( a, b, c )
print background.field
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.