簡體   English   中英

python-從子級調用父方法時保留在基類中

[英]python - remain in base class when calling parent method from child

我有以下基類:

class ClientRepo(Repository):

    def __init__(self) -> None:
        self.__clientList = []

    def hasClientWithId(self, clientId):
        for client in self.__clientList:
            if client.getId() == clientId:
                return True
        return False

    def addClient(self, client):
        if type(client).__name__ == 'ClientDAO':
            if not self.hasClientWithId(client.getId()):
                client.setClientId(self.__maximumIndexInClientList() + 1)
                self.__clientList.append(client)
            else:
                raise ObjectAlreadyInCollectionException
        else:
            raise TypeError

它基本上只包含一個列表,並且可以向其中添加ClientDAO。

以下是從中得出的:

class ClientFileRepository(ClientRepo):

    def __init__(self, fileName) -> None:
        super().__init__()
        self.__fileName = fileName
        self.__file = None

    def hasClientWithId(self, clientId):
        self.__loadRepo()
        hasClientWithId = super().hasClientWithId(clientId)
        super().clean()
        return hasClientWithId

    def addClient(self, client):
        self.__loadRepo()
        super().addClient(client)
        self.__storeRepo()
        super().clean()

    def __loadFileReadMode(self):
        self.__file = open(self.__fileName, "r")

    def __loadFileWriteMode(self):
        self.__file = open(self.__fileName, "w")

    def __closeFile(self):
        self.__file.close()

    def __loadRepo(self):
        self.__loadFileReadMode()
        for line in self.__file:
            splitLine = line.split()
            clientToAdd = ClientDAO(splitLine[1])
            clientToAdd.setClientId(int(splitLine[0]))
            super().addClientWithId(clientToAdd)
        self.__closeFile()

    def __storeRepo(self):
        self.__loadFileWriteMode()
        self.__file.write("")
        for client in super().getList():
            self.__file.write(self.clientToString(client))
        self.__closeFile()

    def clientToString(self, clientDAO):
        return str(clientDAO.getId()) + " " + clientDAO.getName() + "\n"

一個應該從文件加載列表,從父級調用addClient並將更新后的列表存儲在文件中的類。 問題在於,子類在addClient加載文件后,它將調用父級中的方法,該方法再次從子級中調用hasClientWithId 但是我希望它從父級調用hasClientWithId ,即它所在的上下文。我可以實現嗎?

我可以想到幾種實現目標的方法。 我把他們從最壞到最好排名

1.正是您要求的

您希望ClientRepo.addClient調用ClientRepo.hasClientWithId而不是ClientFileRepository.hasClientWithId 可以強制執行以下操作:

class ClientRepo(Repository): 
    def addClient(self, client): 
        if type(client).__name__ == 'ClientDAO': 
            if not ClientRepo.hasClientWithId(self, client.getId()): 
                client.setClientId(self.__maximumIndexInClientList() + 1)
                self.__clientList.append(client) 
            else: 
                raise ObjectAlreadyInCollectionException
        else: 
            raise TypeError

這不是一個好方法,因為它不直觀並且破壞了OOP的原理。 任何其他編寫重寫了hasClientWithIdClientRepo子類的hasClientWithId都希望這將對每次調用hasClientWithId都有效,即使在addClient內部

2.讓ClientFileRepository決定使用哪個函數

添加一個變量

self.__isFileOpen = False

ClientFileRepository.__init__ ,打開文件時將其設置為True ,關閉文件時將其設置為False 然后改變hasClientWithIdClientFileRepository

def hasClientWithId(self, clientId):
    if not self.__isFileOpen:
        self.__loadRepo()
        result = super().hasClientWithId(clientId)
        super().clean() 
        return result
    else:
        return super().hasClientWithId(clientId)

以避免再次打開同一文件。 這行得通,但是為此類編寫新函數非常困難,因為您始終需要知道函數調用是來自類內部還是其他地方的調用。 同樣,這似乎效率很低,因為即使只添加一個客戶端,您也會讀寫整個文件。

3.僅讀取一次文件,然后修改基礎的ClientRepo

class ClientFileRepository(ClientRepo): 
    def __init__(self, fileName) -> None: 
        super().__init__() 
        self.__fileName = fileName 
        self.__loadRepo()

    # No hasClientWithId needed

    def addClient(self, client):
        super().addClient(client)
        self.__storeRepo()

    def __loadRepo(self):
        with open(self.__filename) as file:
            for line in file: 
                splitLine = line.split() 
                clientToAdd = ClientDAO(splitLine[1])
                clientToAdd.setClientId(int(splitLine[0])) 
                super().addClientWithId(clientToAdd) 

    def __storeRepo(self): 
        with open(self.__filename, "w") as file:
            file.write("") 
            for client in super().getList():
                file.write(self.clientToString(client))

顯然,這假定該文件在調用addClient之間沒有被其他人更改,並且該程序仍會為每個addClient覆蓋整個文件。 如果這對您來說是個問題,則最好是明確的並使loadRepostoreRepo公開。 然后,使用此類的程序員可以決定何時進行加載和保存是必要且有用的。 您可以為此使用上下文管理器。

額外:讀取並保存每種方法的文件

您可以使用函數裝飾器來使用解決方案2,而無需為每個函數編寫相同的代碼:

import functools

def loadAndStore(function):
    @functoools.wraps(function)
    def wrappedFunction(self, *args, **kwargs):
        if self.__isFileOpen:
            return function(self, *args, **kwargs)
        else:
            self.__isFileOpen = True
            self.__loadRepo()
            try:
                return function(self, *args, **kwargs)
            except Exception as e: # Only catch expected exceptions
                raise
            finally:
                self.__storeRepo()
                self.clear() # some cleanup
                self.__isFileOpen = False
    return wrappedFunction

class ClientFileRepository(ClientRepo): 
    def __init__(self, fileName) -> None: 
        super().__init__() 
        self.__fileName = fileName
        self.__isFileOpen = False

    @loadAndStore
    def hasClientWithId(self, clientId):
        return super().hasClientWithId(clientId)

    @loadAndStore
    def addClient(self, client):
        super().addClient(client)

    def __loadRepo(self):
        with open(self.__filename) as file:
            for line in file: 
                splitLine = line.split() 
                clientToAdd = ClientDAO(splitLine[1])
                clientToAdd.setClientId(int(splitLine[0])) 
                super().addClientWithId(clientToAdd) 

    def __storeRepo(self): 
        with open(self.__filename, "w") as file:
            file.write("") 
            for client in super().getList():
                file.write(self.clientToString(client))

這里要小心,使用它不是很直觀。 例如self.__isFileOpen是在__init__定義的,但是下面的方法都沒有直接使用它。 相反,它的使用隱藏在loadAndStore裝飾器中。

最后一些提示:

  • type(client).__name__ == 'ClientDAO'是不好的做法。 使用isinstance(client, ClientDAO)完全采用OOP
  • 如果這不是給定命名約定的更大項目的一部分,請使用python樣式指南
  • 通常認為不需要使用諸如__fileName類的私有變量,只需在變量前加一個下划線即可表示“內部使用”。 函數也是如此。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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