当从子对象调用父方法时,python仍保留在基类中

2024-06-26 02:20:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我有以下基类:

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,从父级,也就是它所在的上下文。我能做到吗?你知道吗


Tags: inselfclientnoneforreturninitdef
1条回答
网友
1楼 · 发布于 2024-06-26 02:20:27

我能想出几种方法来实现你的目标。我把他们从最差排到最好

<强>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

这不是一个好方法,因为它不直观,并且违反了面向对象的原则。任何其他编写重写hasClientWithIdClientRepo子类的程序员都会期望这会对hasClientWithId的每个调用产生影响,甚至在addClient内部

<强>2。让ClientFileRepository决定使用哪个函数

添加变量

self.__isFileOpen = False

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

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
  • 如果这不是具有给定命名约定的大型项目的一部分,请使用the python style guide
  • 使用像__fileName这样的私有变量通常被认为是不必要的,只要在变量前面加一个下划线就可以表示“内部使用”。函数也是如此。你知道吗

相关问题 更多 >