簡體   English   中英

Python,重寫繼承的類方法

[英]Python, Overriding an inherited class method

我有兩個類, FieldBackground 他們看起來有點像這樣:

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找到了BackgroundbuildField方法,而不是Field的。

由於Background.buildField需要2個參數而不是1個,

self.field = self.buildField()

引發錯誤。


那么我們如何告訴Python調用FieldbuildField方法而不是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類中的那個。這是因為,這里的selfBackground類的實例(嘗試在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 decoratorstaticmethodbuildField()分離為屬於該類。

但是,如果基類構造函數未從內部調用任何實例方法,則可以安全地覆蓋此基類的任何方法。

談論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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM