python-从子级调用父方法时保留在基类中

时间:2018-11-30 19:52:06

标签: python oop inheritance

我有以下基类:

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 个答案:

答案 0 :(得分:1)

我可以想到几种实现目标的方法。我对它们的排名从最坏到最好

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的原理。任何其他编写覆盖ClientRepo的{​​{1}}子类的程序员都希望,即使在hasClientWithId内,每次调用hasClientWithId也会有影响

2。让addClient决定使用哪个功能

添加变量

ClientFileRepository
self.__isFileOpen = False

,在打开文件时将其设置为ClientFileRepository.__init__,在关闭文件时将其设置为True。然后将False中的hasClientWithId更改为

ClientFileRepository

,以避免再次打开同一文件。这行得通,但是为此类编写新函数非常困难,因为您始终需要知道函数调用是来自类内部还是其他地方的调用。同样,这似乎效率很低,因为即使只添加一个客户端,您也会读写整个文件。

3。仅读取一次文件,然后修改基础的def hasClientWithId(self, clientId): if not self.__isFileOpen: self.__loadRepo() result = super().hasClientWithId(clientId) super().clean() return result else: return super().hasClientWithId(clientId)

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覆盖了整个文件。如果这对您来说是个问题,则最好明确地公开addClientloadRepo。然后,使用此类的程序员可以决定何时进行加载和保存是必要且有用的。您可以为此使用上下文管理器。

其他:阅读并保存每种方法的文件

您可以使用函数修饰符来使用解决方案2,而无需为每个函数编写相同的代码:

storeRepo

在这里要小心,使用它不是很直观。例如,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'完全采用OOP
  • 如果这不是给定命名约定的更大项目的一部分,请使用the python style guide
  • 通常认为不需要使用诸如isinstance(client, ClientDAO)之类的私有变量,只需在变量前面加上一个下划线即可表示“内部使用”。函数也是如此。