Atslēdziet Python abstraktās bāzes klases (ABC). Uzziniet kritisko atšķirību starp protokolu balstītu strukturālo tipēšanu un formālu interfeisa dizainu.
Python Abstract Base Classes: Protokolu ieviešanas un interfeisa dizaina apgūšana
Programmatūras izstrādes pasaulē galvenais mērķis ir veidot izturīgas, viegli uzturamas un mērogojamas lietojumprogrammas. Tā kā projekti izaug no dažiem skriptiem līdz sarežģītām sistēmām, ko pārvalda starptautiskas komandas, skaidras struktūras un paredzamu līgumu nepieciešamība kļūst ārkārtīgi svarīga. Kā mēs varam nodrošināt, ka dažādas sastāvdaļas, ko, iespējams, raksta dažādi izstrādātāji dažādās laika zonās, var nevainojami un uzticami mijiedarboties? Atbilde slēpjas abstrakcijas principā.
Python, ar savu dinamisko dabu, ir slavena abstrakcijas filozofija: "duck typing". Ja objekts staigā kā pīle un klaigā kā pīle, mēs izturamies pret to kā pret pīli. Šī elastība ir viena no Python lielākajām priekšrocībām, veicinot ātru izstrādi un tīru, lasāmu kodu. Tomēr liela mēroga lietojumprogrammās, paļaujoties tikai uz netiešiem vienošanās, var rasties smalkas kļūdas un uzturēšanas problēmas. Kas notiek, ja "pīle" negaidīti nevar lidot? Šeit savu vietu ieņem Python Abstract Base Classes (ABC), nodrošinot jaudīgu mehānismu formālu līgumu izveidošanai, nezaudējot Python dinamisko garu.
Taču šeit ir būtiska un bieži vien pārprasta atšķirība. ABC Python nav universāls rīks. Tie kalpo divām atšķirīgām, spēcīgām programmatūras dizaina filozofijām: skaidru, formālu interfeisu izveidošana, kas prasa mantošanu, un elastīgu protokolu definēšana, kas pārbauda spējas. Izpratne par atšķirību starp šīm divām pieejām — interfeisa dizains pret protokola ieviešanu — ir galvenais, lai atraisītu pilnu objektorientēta dizaina potenciālu Python un rakstītu kodu, kas ir gan elastīgs, gan drošs. Šī rokasgrāmata izpētīs abas filozofijas, sniedzot praktiskus piemērus un skaidrus norādījumus par to, kad katra pieeja jāizmanto jūsu globālajos programmatūras projektos.
Piezīme par formatēšanu: Lai ievērotu īpašus formatēšanas ierobežojumus, piemēri kodā šajā rakstā ir attēloti standarta teksta tagu ietvaros, izmantojot treknrakstu un slīprakstu stilus. Mēs iesakām tos kopēt savā redaktorā, lai nodrošinātu vislabāko lasāmību.
Pamats: Kas īsti ir Abstract Base Classes?
Pirms iedziļināties abās dizaina filozofijās, noskaidrosim stabilu pamatu. Kas ir Abstract Base Class? Tās pamatā ABC ir citu klases paraugs. Tā definē metožu un rekvizītu kopumu, kas jebkurai atbilstošai apakšklasei ir jāievieš. Tas ir veids, kā pateikt: "Jebkurai klasei, kas apgalvo, ka ir daļa no šīs ģimenes, ir jābūt noteiktām spējām."
Python iebūvētais `abc` modulis nodrošina rīkus ABC izveidei. Divi galvenie komponenti ir:
- `ABC`: Palīgklase, ko izmanto kā metaklasi ABC izveidei. Mūsdienu Python (3.4+) versijā varat vienkārši mantot no `abc.ABC`.
- `@abstractmethod`: Dekorators, ko izmanto, lai atzīmētu metodes kā abstraktas. Jebkurai ABC apakšklasei ir jāievieš šīs metodes.
Ir divi pamatnoteikumi, kas reglamentē ABC:
- Jūs nevarat izveidot ABC instanci, kurai ir neieviesušās abstraktās metodes. Tā ir veidne, nevis pabeigts produkts.
- Jebkurai konkrētai apakšklasei ir jāievieš visas iemantotās abstraktās metodes. Ja tas netiek izdarīts, tā arī kļūst par abstraktu klasi, un jūs nevarat izveidot tās instanci.
Redzēsim to darbībā ar klasisku piemēru: mediju failu apstrādes sistēmu.
Piemērs: Vienkāršs MediaFile ABC
Iedomājieties, ka veidojam lietojumprogrammu, kurai jāapstrādā dažādi mediju veidi. Mēs zinām, ka katram mediju failam, neatkarīgi no tā formāta, vajadzētu būt atskaņojamam un saturēt kādu metadatu. Mēs varam definēt šo līgumu ar ABC.
import abc
class MediaFile(abc.ABC):
def __init__(self, filepath: str):
self.filepath = filepath
print(f"Bāzes inicializācija priekš {self.filepath}")
@abc.abstractmethod
def play(self) -> None:
"""Atskaņot mediju failu."""
raise NotImplementedError
@abc.abstractmethod
def get_metadata(self) -> dict:
"""Atgriezt vārdnīcu ar mediju metadatiem."""
raise NotImplementedError
Ja mēs mēģināsim izveidot `MediaFile` instanci tieši, Python mūs apturēs:
# Tas izraisīs TypeError
# media = MediaFile("path/to/somefile.txt")
# TypeError: Can't instantiate abstract class MediaFile with abstract methods get_metadata, play
Lai izmantotu šo paraugu, mums ir jāizveido konkrētas apakšklases, kas nodrošina `play()` un `get_metadata()` ieviešanu.
class AudioFile(MediaFile):
def play(self) -> None:
print(f"Atskaņo audio no {self.filepath}...")
def get_metadata(self) -> dict:
return {"codec": "mp3", "duration_seconds": 180}
class VideoFile(MediaFile):
def play(self) -> None:
print(f"Atskaņo video no {self.filepath}...")
def get_metadata(self) -> dict:
return {"codec": "h264", "resolution": "1920x1080"}
Tagad mēs varam izveidot `AudioFile` un `VideoFile` instances, jo tās izpilda `MediaFile` definēto līgumu. Tas ir ABC pamatmehānisms. Bet patiesā spēks nāk no tā, kā mēs šo mehānismu izmantojam.
Pirmā filozofija: ABC kā Formāla Interfeisa Dizains (Nominālā Tipēšana)
Pirmais un tradicionālākais veids, kā izmantot ABC, ir formālam interfeisa dizainam. Šī pieeja ir balstīta uz nominālo tipēšanu, kas ir pazīstama koncepcija izstrādātājiem, kas nāk no valodām, piemēram, Java, C++ vai C#. Nominālajā sistēmā tipa saderību nosaka tā nosaukums un skaidra deklarācija. Šajā kontekstā klase tiek uzskatīta par `MediaFile` tikai tad, ja tā tieši manto no `MediaFile` ABC.
Domājiet par to kā par profesionālu sertifikātu. Lai būtu sertificēts projektu vadītājs, jūs nevarat vienkārši izlikties par tādu; jums ir jāstudē, jāiziet konkrēts eksāmens un jāsaņem oficiāls sertifikāts, kas skaidri norāda jūsu kvalifikāciju. Jūsu sertifikāta nosaukums un izcelsme ir svarīga.
Šajā modelī ABC darbojas kā nenegocējams līgums. Mantojot no tā, klase sniedz sistēmai formālu solījumu, ka tā nodrošinās nepieciešamo funkcionalitāti.
Piemērs: Datu Eksportētāju Sistēma
Iedomājieties, ka veidojam sistēmu, kas ļauj lietotājiem eksportēt datus dažādos formātos. Mēs vēlamies nodrošināt, ka katrs eksportētāja spraudnis atbilst stingrai struktūrai. Mēs varam definēt `DataExporter` interfeisu.
import abc
from datetime import datetime
class DataExporter(abc.ABC):
"""Formāls interfeiss datu eksportējošām klasēm."""
@abc.abstractmethod
def export(self, data: list[dict]) -> str:
"""Eksportē datus un atgriež statusa ziņojumu."""
pass
def get_timestamp(self) -> str:
"""Konkrēta palīgmetode, ko kopīgo visas apakšklases."""
return datetime.utcnow().isoformat()
class CSVExporter(DataExporter):
def export(self, data: list[dict]) -> str:
filename = f"export_{self.get_timestamp()}.csv"
print(f"Eksportē {len(data)} rindas uz {filename}")
# ... faktiskā CSV rakstīšanas loģika ...
return f"Successfully exported to {filename}"
class JSONExporter(DataExporter):
def export(self, data: list[dict]) -> str:
filename = f"export_{self.get_timestamp()}.json"
print(f"Eksportē {len(data)} ierakstus uz {filename}")
# ... faktiskā JSON rakstīšanas loģika ...
return f"Successfully exported to {filename}"
Šeit `CSVExporter` un `JSONExporter` ir skaidri un verificējami kā `DataExporter`i. Mūsu lietojumprogrammas galvenā loģika var droši paļauties uz šo līgumu:
def run_export_process(exporter: DataExporter, data_to_export: list[dict]):
print("--- Eksportēšanas procesa sākums ---")
if not isinstance(exporter, DataExporter):
raise TypeError("Eksportētājam jābūt derīgam DataExporter ieviešanai.")
status = exporter.export(data_to_export)
print(f"Process pabeigts ar statusu: {status}")
# Lietošana
data = [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}]
run_export_process(CSVExporter(), data)
run_export_process(JSONExporter(), data)
Pamanāms, ka ABC nodrošina arī konkrētu metodi `get_timestamp()`, kas piedāvā kopīgu funkcionalitāti visiem tās bērniem. Tas ir izplatīts un spēcīgs raksts interfeisa dizainā.
Formālā Interfeisa Pieejas Priekšrocības un Trūkumi
Priekšrocības:
- Nepārprotams un Skaidrs: Līgums ir kristāldzidrs. Izstrādātājs var redzēt mantojuma rindu `class CSVExporter(DataExporter):` un uzreiz saprast klases lomu un spējas.
- Rīkiem Draudzīgs: IDE, lintēri un statiskās analīzes rīki var viegli pārbaudīt līgumu, nodrošinot lielisku automātisko pabeigšanu un kļūdu pārbaudi.
- Kopīga Funkcionalitāte: ABC var nodrošināt konkrētas metodes, darboties kā patiesa bāzes klase un samazināt koda dublēšanos.
- Pazīstamība: Šis raksts ir uzreiz atpazīstams izstrādātājiem no lielākās daļas citām objektorientētām valodām.
Trūkumi:
- Stipra Sasaiste: Konkrētā klase tagad ir tieši saistīta ar ABC. Ja ABC ir jāpārvieto vai jāmaina, tas ietekmē visas apakšklases.
- Stingrība: Tas piespiež stingru hierarhisku attiecību. Ko darīt, ja klase loģiski varētu darboties kā eksportētājs, bet jau manto no citas, būtiskas bāzes klases? Python vairāku mantojuma var atrisināt šo problēmu, taču tas var radīt arī savas sarežģītības (piemēram, dimanta problēmu).
- Invazīvs: To nevar izmantot, lai adaptētu trešās puses kodu. Ja jūs izmantojat bibliotēku, kas nodrošina klasi ar `export()` metodi, jūs nevarat padarīt to par `DataExporter` bez tās mantošanas (kas var nebūt iespējams vai vēlams).
Otrā filozofija: ABC kā Protokolu Ieviešana (Strukturālā Tipēšana)
Otrā, "Pythoniskākā" filozofija atbilst duck typing. Šī pieeja izmanto strukturālo tipēšanu, kur saderību nosaka nevis nosaukums vai izcelsme, bet gan struktūra un uzvedība. Ja objektam ir nepieciešamās metodes un atribūti, lai veiktu darbu, tas tiek uzskatīts par pareizo tipu šim darbam, neatkarīgi no tā deklarētās klases hierarhijas.
Domājiet par spēju peldēt. Lai tiktu uzskatīts par peldētāju, jums nav nepieciešams sertifikāts vai jābūt daļai no "Peldētāju" ciltskokam. Ja jūs varat sevi pārvietot pa ūdeni, nenogrimstot, jūs esat strukturāli peldētājs. Cilvēks, suns un pīle visi var būt peldētāji.
ABC var izmantot, lai formalizētu šo koncepciju. Tā vietā, lai piespiestu mantošanu, mēs varam definēt ABC, kas atpazīst citas klases kā savas virtuālās apakšklases, ja tās ievieš nepieciešamo protokolu. Tas tiek panākts, izmantojot īpašu maģisko metodi: `__subclasshook__`.
Kad izsaucat `isinstance(obj, MyABC)` vai `issubclass(SomeClass, MyABC)`, Python vispirms pārbauda tiešu mantošanu. Ja tas neizdodas, tas pārbauda, vai `MyABC` ir `__subclasshook__` metode. Ja tā ir, Python to izsauc, jautājot: "Hei, vai jūs uzskatāt šo klasi par savu apakšklasi?" Tas ļauj ABC definēt savus dalības kritērijus, pamatojoties uz struktūru.
Piemērs: `Serializable` Protokols
Definēsim protokolu objektiem, kas var tikt serializēti uz vārdnīcu. Mēs nevēlamies piespiest katru serializējamo objektu mūsu sistēmā mantot no kopīgas bāzes klases. Tās var būt datu bāzu modeļi, datu pārsūtīšanas objekti vai vienkārši konteineri.
import abc
class Serializable(abc.ABC):
@abc.abstractmethod
def to_dict(self) -> dict:
pass
@classmethod
def __subclasshook__(cls, C):
if cls is Serializable:
# Pārbaudiet, vai 'to_dict' ir C metožu izpildes secībā
if any("to_dict" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
Tagad izveidosim dažas klases. Svarīgi, neviena no tām nemantos no `Serializable`.
class User:
def __init__(self, name: str, email: str):
self.name = name
self.email = email
def to_dict(self) -> dict:
return {"name": self.name, "email": self.email}
class Product:
def __init__(self, sku: str, price: float):
self.sku = sku
self.price = price
# Šī klase NEATBILST protokolam
class Configuration:
def __init__(self, setting: str):
self.setting = setting
Pārbaudīsim tās pret mūsu protokolu:
print(f"Vai User ir serializējams? {isinstance(User('Test', 't@t.com'), Serializable)}")
print(f"Vai Product ir serializējams? {isinstance(Product('T-1000', 99.99), Serializable)}")
print(f"Vai Configuration ir serializējams? {isinstance(Configuration('ON'), Serializable)}")
# Izvade:
# Vai User ir serializējams? True
# Vai Product ir serializējams? False <- Kāpēc? Labosim to.
# Vai Configuration ir serializējams? False
Ak, interesanta kļūda! Mūsu `Product` klasei nav `to_dict` metodes. Pievienosim to.
class Product:
def __init__(self, sku: str, price: float):
self.sku = sku
self.price = price
def to_dict(self) -> dict: # Metodes pievienošana
return {"sku": self.sku, "price": self.price}
print(f"Vai Product tagad ir serializējams? {isinstance(Product('T-1000', 99.99), Serializable)}")
# Izvade:
# Vai Product tagad ir serializējams? True
Pat ja `User` un `Product` nedala kopīgu bāzes klasi (izņemot `object`), mūsu sistēma var tos abus uzskatīt par `Serializable`, jo tie izpilda protokolu. Tas ir neticami jaudīgs atsaistīšanai.
Protokola Pieejas Priekšrocības un Trūkumi
Priekšrocības:
- Maksimāla Elastība: Veicina ārkārtīgi brīvu sasaisti. Komponentas interesē tikai uzvedība, nevis implementācijas izcelsme.
- Adaptācija: Tas ir ideāli piemērots esošā koda adaptēšanai, īpaši no trešo pušu bibliotēkām, lai tie atbilstu jūsu sistēmas interfeisiem, nemainot sākotnējo kodu.
- Veicina Kompozīciju: Veicina dizaina stilu, kurā objekti tiek veidoti no neatkarīgām spējām, nevis caur dziļām, stingrām mantošanas kopnēm.
Trūkumi:
- Netiešs Līgums: Attiecības starp klasi un protokolu, ko tā ievieš, nav uzreiz acīmredzamas no klases definīcijas. Izstrādātājam varētu būt nepieciešams meklēt kodu bāzi, lai saprastu, kāpēc `User` objekts tiek uzskatīts par `Serializable`.
- Darbības Laika Lielāka Slodze: `isinstance` pārbaude var būt lēnāka, jo tā ir jāizsauc `__subclasshook__` un jāveic pārbaudes uz klases metodēm.
- Potenciāls Sarežģītībai: `__subclasshook__` iekšējā loģika var kļūt diezgan sarežģīta, ja protokols ietver vairākas metodes, argumentus vai atgriežamās vērtības.
Modernā sintēze: `typing.Protocol` un Statiskā Analīze
Tā kā Python lietošana liela mēroga sistēmās pieauga, tāpat pieauga arī vēlme pēc labākas statiskās analīzes. `__subclasshook__` pieeja ir jaudīga, bet ir tikai darbības laika mehānisms. Ko darīt, ja mēs varētu iegūt strukturālās tipēšanas priekšrocības pirms mēs pat izpildām kodu?
Tas noveda pie `typing.Protocol` (PEP 544) ieviešanas. Tas nodrošina standartizētu un elegantu veidu, kā definēt protokolus, kas galvenokārt paredzēti statiskajiem tipu pārbaudītājiem, piemēram, Mypy, Pyright vai PyCharm inspektoram.
A `Protocol` klase darbojas līdzīgi kā mūsu `__subclasshook__` piemērs, bet bez papildu darba. Jūs vienkārši definējat metodes un to parakstus. Jebkura klase ar atbilstošām metodēm un parakstiem tiks uzskatīta par strukturāli saderīgu ar statisko tipu pārbaudītāju.
Piemērs: `Quacker` Protokols
Atsauksimies uz klasisko duck typing piemēru, bet ar modernām rīcības pārbaudēm.
from typing import Protocol
class Quacker(Protocol):
def quack(self, volume: int) -> str:
"""Ražo klaigāšanas skaņu."""
... # Piezīme: protokola metodes ķermenis nav nepieciešams
class Duck:
def quack(self, volume: int) -> str:
return f"KLAIGĀ! (ar skaļumu {volume})"
class Dog:
def bark(self, volume: int) -> str:
return f"RĀU! (ar skaļumu {volume})"
def make_sound(animal: Quacker):
print(animal.quack(10))
make_sound(Duck()) # Statiskā analīze iziet
make_sound(Dog()) # Statiskā analīze neizdodas!
Ja palaidīsit šo kodu caur tipu pārbaudītāju, piemēram, Mypy, tas izcels `make_sound(Dog())` rindu ar kļūdu: `Argument 1 to "make_sound" has incompatible type "Dog"; expected "Quacker"`. Tipu pārbaudītājs saprot, ka `Dog` neizpilda `Quacker` protokolu, jo tam trūkst `quack` metodes. Tas noķer kļūdu pirms koda izpildes.
Runtime Protokoli ar `@runtime_checkable`
Pēc noklusējuma `typing.Protocol` ir paredzēts tikai statiskai analīzei. Ja mēģināsit to izmantot darbības laikā `isinstance` pārbaudē, saņemsit kļūdu.
# isinstance(Duck(), Quacker) # -> TypeError: Protocol 'Quacker' cannot be instantiated
Tomēr jūs varat savienot statisko analīzi un darbības laika uzvedību ar `@runtime_checkable` dekoratoru. Tas būtībā liek Python automātiski ģenerēt `__subclasshook__` loģiku.
from typing import Protocol, runtime_checkable
@runtime_checkable
class Quacker(Protocol):
def quack(self, volume: int) -> str: ...
class Duck:
def quack(self, volume: int) -> str: return "..."
print(f"Vai Duck ir Quacker instances? {isinstance(Duck(), Quacker)}")
# Izvade:
# Vai Duck ir Quacker instances? True
Tas sniedz jums abas labākās pasaules: tīras, deklaratīvas protokolu definīcijas statiskai analīzei un iespēju veikt darbības laika pārbaudes, kad nepieciešams. Tomēr ņemiet vērā, ka protokolu darbības laika pārbaudes ir lēnākas nekā standarta `isinstance` izsaukumi, tāpēc tās jāizmanto apdomīgi.
Praktiska Lēmumu Pieņemšana: Globālā Izstrādātāja Ceļvedis
Tātad, kuru pieeju jums vajadzētu izvēlēties? Atbilde pilnībā ir atkarīga no jūsu konkrētās lietošanas gadījuma. Šeit ir praktisks ceļvedis, pamatojoties uz izplatītiem scenārijiem starptautiskos programmatūras projektos.
Scenārijs 1: Spraudņu Arhitektūras Veidošana Globālam SaaS Produktam
Jūs projektējat sistēmu (piemēram, e-komercijas platformu, CMS), ko papildinās pirmās un trešās puses izstrādātāji visā pasaulē. Šiem spraudņiem ir nepieciešama cieša integrācija ar jūsu galveno lietojumprogrammu.
- Ieteikums: Formāls Interfeiss (Nominālā `abc.ABC`).
- Pamatojums: Skaidrība, stabilitāte un nepārprotamība ir vissvarīgākā. Jums ir nepieciešams nenegocējams līgums, kam spraudņu izstrādātājiem ir apzināti jāpievienojas, mantojot no jūsu `BasePlugin` ABC. Tas padara jūsu API nepārprotamu. Jūs varat arī nodrošināt būtiskas palīgmetodes (piemēram, reģistrēšanai, konfigurācijas piekļuvei, internacionalizācijai) bāzes klasē, kas ir milzīgs ieguvums jūsu izstrādātāju ekosistēmai.
Scenārijs 2: Finanšu Datu Apstrāde no Vairākām, Nesaistītām API
Jūsu finanšu tehnoloģiju lietojumprogrammai ir jāpatērē darījumu dati no dažādām globālām maksājumu vārtejām: Stripe, PayPal, Adyen un, iespējams, reģionāls pakalpojumu sniedzējs, piemēram, Mercado Pago Latīņamerikā. Objektus, ko atgriež viņu SDK, pilnībā nekontrolējat.
- Ieteikums: Protokols (`typing.Protocol`).
- Pamatojums: Jūs nevarat modificēt šo trešo pušu SDK avota kodu, lai tās mantotu no jūsu `Transaction` bāzes klases. Tomēr jūs zināt, ka katram viņu darījumu objektam ir metodes, piemēram, `get_id()`, `get_amount()` un `get_currency()`, pat ja tās ir nosauktas nedaudz atšķirīgi. Jūs varat izmantot Adaptera dizaina paraugu kopā ar `TransactionProtocol`, lai izveidotu vienotu skatījumu. Protokols ļauj definēt nepieciešamo datu formu, ļaujot jums rakstīt apstrādes loģiku, kas darbojas ar jebkuru datu avotu, ja vien to var adaptēt, lai atbilstu protokolam.
Scenārijs 3: Lielas, Monolītas Mantojuma Lietojumprogrammas Pārstrāde
Jums ir uzdots sadalīt mantojuma monolītu mūsdienu mikropakalpojumos. Esošais kodu kopums ir atkarību mudžeklis, un jums ir jāievieš skaidras robežas, nepārrakstot visu uzreiz.
- Ieteikums: Kombinācija, bet stipri paļaujieties uz Protokoliem.
- Pamatojums: Protokoli ir izcils rīks pakāpeniskai pārstrādei. Jūs varat sākt, definējot ideālos interfeisus starp jaunajiem pakalpojumiem, izmantojot `typing.Protocol`. Pēc tam jūs varat rakstīt adapterus mantojuma daļām, lai tās atbilstu šiem protokoliem, nemainot galveno mantojuma kodu uzreiz. Tas ļauj jums pakāpeniski atsaistīt komponentes. Kad komponente ir pilnībā atsaistīta un sazinās tikai caur protokolu, tā ir gatava izvilkšanai savā pakalpojumā. Formālie ABC vēlāk varētu tikt izmantoti, lai definētu kodola modeļus jaunajos, tīrajos pakalpojumos.
Secinājums: Ievinot Abstrakciju Savā Kodā
Python Abstract Base Classes ir apliecinājums valodas pragmatiskajam dizainam. Tās nodrošina izsmalcinātu rīku kopumu abstrakcijai, kas respektē gan tradicionālās objektorientētās programmēšanas strukturālo disciplīnu, gan duck typing dinamisko elastību.
Ceļš no netiešas vienošanās līdz formālam līgumam ir nobrieduša kodu kopuma pazīme. Izprotot divas ABC filozofijas, jūs varat pieņemt informētus arhitektūras lēmumus, kas nodrošina tīrākas, vieglāk uzturamas un ļoti mērogojamas lietojumprogrammas.
Lai apkopotu galvenos secinājumus:
- Formālais Interfeisa Dizains (Nominālā Tipēšana): Izmantojiet `abc.ABC` ar tiešu mantošanu, kad nepieciešams skaidrs, nepārprotams un atklājams līgums. Tas ir ideāli piemērots sistēmām, spraudņu sistēmām un situācijām, kurās jūs kontrolējat klases hierarhiju. Tas ir par to, kas klase ir pēc deklarācijas.
- Protokolu Ieviešana (Strukturālā Tipēšana): Izmantojiet `typing.Protocol`, kad nepieciešama elastība, atsaistīšana un iespēja adaptēt esošu kodu. Tas ir ideāli piemērots darbam ar ārējām bibliotēkām, mantojuma sistēmu pārstrādei un uzvedības polimorfisma dizainam. Tas ir par to, ko klase var darīt pēc tās struktūras.
Izvēle starp interfeisu un protokolu nav tikai tehniska detaļa; tas ir fundamentāls dizaina lēmums, kas veidos jūsu programmatūras attīstību. Apgūstot abus, jūs aprīkojat sevi, lai rakstītu Python kodu, kas ir ne tikai jaudīgs un efektīvs, bet arī elegants un izturīgs pret pārmaiņām.