[英]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的原理。 任何其他編寫重寫了hasClientWithId
的ClientRepo
子類的hasClientWithId
都希望這將對每次調用hasClientWithId
都有效,即使在addClient
內部
2.讓ClientFileRepository
決定使用哪個函數
添加一個變量
self.__isFileOpen = False
在ClientFileRepository.__init__
,打開文件時將其設置為True
,關閉文件時將其設置為False
。 然后改變hasClientWithId
內ClientFileRepository
到
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
覆蓋整個文件。 如果這對您來說是個問題,則最好是明確的並使loadRepo
和storeRepo
公開。 然后,使用此類的程序員可以決定何時進行加載和保存是必要且有用的。 您可以為此使用上下文管理器。
額外:讀取並保存每種方法的文件
您可以使用函數裝飾器來使用解決方案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 __fileName
類的私有變量,只需在變量前加一個下划線即可表示“內部使用”。 函數也是如此。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.