環境
我的朋友告訴我,在 OOP 中,您通常不想修改現有代碼庫中的任何抽象基類,因為這意味著您必須對每個派生類實作新的更改。我感興趣的是,人們更喜歡以 Python 的方式對代碼庫進行哪種修改。重點是改變現有的代碼庫。
示例場景
我有一個名為 的抽象基類Animal,因為使用這個庫的代碼必須與Animal物件互動。我有多個子類實作:Dog每個子類Cat都有一組不同的欄位,每個欄位都需要它們來實作自己的內部功能。所以此時代碼庫看起來像:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
修改
實作此功能后,開發人員意識到他們想要記錄Animal物件的相關欄位(或狀態),并且記錄的資料因子類而異。
選項 A
最初,我的想法是實作另一個abstractmethod并以這種方式添加功能。這迫使每個人以他們需要的任何方式Animal實施新的。get_fields()
class Animal(ABC):
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
@abstractmethod
def get_fields(self) -> list:
raise NotImplementedError
class Dog(Animal):
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
def get_fields(self) -> list:
return [self.woof]
class Cat(Animal):
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
def get_fields(self) -> list:
return [self.meow, self.purr]
選項 B
我的朋友說我們不應該修改抽象類,但是我們想出的唯一其他選擇是執行以下操作:
def get_field(animal: Animal) -> list:
if isinstance(animal, Dog):
return [animal.woof]
elif isinstance(animal, Cat):
return [animal.meow, animal.purr]
else:
raise TypeError
你會和哪一個一起去?還有另一種更好的方法嗎?哪個更pythonic?
uj5u.com熱心網友回復:
在 ABC 上實作通用機制,作為具體方法,但將配置轉移到子類,并且不要使用硬編碼名稱。
我在Meta這里使用,因為這是您在 Django 模型中看到的東西的型別,命名空間是嵌入式類中給定類的配置Meta。Django 專門使用一個非常相似的系統來跟蹤哪些欄位在用于資料輸入的自動生成的管理面板中顯示在哪里。
from abc import ABC, abstractmethod
from typing import Optional, List
class Animal(ABC):
class Meta:
#could also set to [] as default...
fields_of_interest : Optional[List[str]] = None
@abstractmethod
def feed(self, foo: str) -> None:
raise NotImplementedError
def get_fields(self) -> List:
if self.Meta.fields_of_interest is None:
# could also raise NotImplementedError("need to set `fields_of_interest` on class Meta in class {self.__class__.__name__}")
return []
res = [getattr(self, field) for field in self.Meta.fields_of_interest]
return res
class Dog(Animal):
class Meta:
fields_of_interest = ["woof"]
def __init__(self):
self.woof = "woof"
def feed(self, food: str):
print(f"Dog is being fed {food}. It says {self.woof}")
class Cat(Animal):
class Meta:
fields_of_interest = ["purr", "meow"]
def __init__(self):
self.meow = "meow"
self.purr = "purr"
def feed(self, food: str):
print(f"Cat is being fed {food}. It says {self.meow}")
class Mouse(Animal):
def feed(self, foo: str) -> None:
print(f"{self} feed")
for cls in [Cat, Dog, Mouse]:
animal = cls()
print(f"{animal} {animal.get_fields()}")
輸出:
<__main__.Cat object at 0x1079f67d0> ['purr', 'meow']
<__main__.Dog object at 0x1079f6320> ['woof']
<__main__.Mouse object at 0x1079f67d0> []
此外,就抽象與具體而言,創造性地思考以保持方法行為一致(因此是通用的)而不是過于挑剔是有幫助的。例如,無論是最初的設計模式書還是接手它的書都在談論復合模式,它處理“樹”。好吧,他們說的是,當您在 Leaf 上(沒有孩子)并嘗試迭代其不存在的孩子時,他們可以只回傳一個空串列,而不是拋出例外。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/471704.html
標籤:Python python-3.x 哎呀 抽象类
下一篇:C++進階實體2--員工分組
