Разбери основы на конкретном примере!

В Python абстрактные базовые классы предоставляют схему конкретных классов. Они не содержат реализации. Вместо этого они предоставляют интерфейс и обеспечивают правильную реализацию производных конкретных классов.

  • Невозможно создать экземпляры абстрактных базовых классов. Вместо этого они наследуются и расширяются конкретными подклассами.
  • Подклассы, производные от определенного абстрактного базового класса, должны реализовывать методы и свойства, предоставленные в этом абстрактном базовом классе. В противном случае во время создания объекта возникает ошибка.

Напишем код Python3, содержащий простые примеры реализации абстрактных базовых классов:

from abc import ABCMeta, abstractmethod
class AbstactClassCSV(metaclass = ABCMeta):
 
 def __init__(self, path, file_name):
  self._path = path
  self._file_name = file_name
 @property
 @abstractmethod
 def path(self):
  pass
 @path.setter
 @abstractmethod
 def path(self,value):
  pass
 @property
 @abstractmethod
 def file_name(self):
  pass
 @file_name.setter
 @abstractmethod
 def file_name(self,value):
  pass
 @abstractmethod
 def display_summary(self):
  pass

Определение абстрактного базового класса

Python предоставляет модуль abc для определения абстрактного базового класса. Нам нужно импортировать метакласс ABCMeta и назначить его абстрактному классу, который мы хотим определить.

from abc import ABCMeta, abstractmethod
class AbstactClassCSV(metaclass = ABCMeta):
 .....
 .....

Внутри абстрактного базового класса мы заставляем подклассы реализовывать свойства path и file_name с помощью декоратора @abstractmethod. Обратите внимание, что свойства не реализованы и пусты, поскольку абстрактные классы используются только для определения интерфейса.

 @property
 @abstractmethod
 def path(self):
  pass
 @path.setter
 @abstractmethod
 def path(self,value):
  pass
 @property
 @abstractmethod
 def file_name(self):
  pass
 @file_name.setter
 @abstractmethod
 def file_name(self,value):
  pass

Опять же, с помощью декоратора @abstractmethod мы можем определить абстрактный метод и заставить подклассы реализовать метод display_summary (self).

 @abstractmethod
 def display_summary(self):
  pass

На этом этапе, если мы попытаемся создать экземпляр объекта абстрактного класса AbstactClassCSV, мы получим ошибку.

abc_instantiation = AbstactClassCSV("/Users/erdemisbilen/Lessons/", "data_by_genres.csv")
Output:
Traceback (most recent call last):
  File "ABCExample.py", line 119, in <module>
    abc_instantiation = AbstactClassCSV("/Users/erdemisbilen/Lessons/", "data_by_genres.csv")
TypeError: Can't instantiate abstract class AbstactClassCSV with abstract methods display_summary, file_name, path

Создание конкретного подкласса, производного от абстрактного базового класса

Теперь, когда у нас есть абстрактный базовый класс (AbstactClassCSV), мы можем создать наш подкласс путем наследования.

Ниже я создал конкретный класс CSVGetInfo, унаследовав его от абстрактного класса AbstactClassCSV. Следовательно, я должен строго следовать интерфейсу, предоставляемому абстрактным классом, и правильно реализовать все продиктованные методы и свойства в моем конкретном подклассе.

class CSVGetInfo(AbstactClassCSV):
""" This class displays the summary of the tabular data contained in a CSV file """
 @property
 def path(self):
  """ The docstring for the path property """
  print("Getting value of path")
  return self._path
 @path.setter
 def path(self,value):
  if '/' in value:
   self._path = value
   print("Setting value of path to {}".format(value))
  else:
   print("Error: {} is not a valid path string".format(value))
data_by_genres = CSVGetInfo("/Users/erdemisbilen/Lessons/", "data_by_genres.csv")
Output:
Traceback (most recent call last):
  File "ABCExample.py", line 103, in <module>
    data_by_genres = CSVGetInfo("/Users/erdemisbilen/Lessons/", "data_by_genres.csv")
TypeError: Can't instantiate abstract class CSVGetInfo with abstract methods display_summary, file_name

Чтобы продемонстрировать этот случай, я определил только свойство path и оставил свойства file_name и display_summary не реализованными.

Если я попытаюсь создать экземпляр объекта CSVGetInfo в таком состоянии , возникнет указанная выше ошибка.

Вот как абстрактные классы предотвращают неправильное определение подкласса.

class CSVGetInfo(AbstactClassCSV):
 """ This class displays the summary of the tabular data contained 
 in a CSV file """
 @property
 def path(self):
  """ The docstring for the path property """
  print("Getting value of path")
  return self._path
 @path.setter
 def path(self,value):
  if '/' in value:
   self._path = value
   print("Setting value of path to {}".format(value))
  else:
   print("Error: {} is not a valid path string".format(value))
 @property
 def file_name(self):
  """ The docstring for the file_name property """
  print("Getting value of file_name")
  return self._file_name
 @file_name.setter
 def file_name(self,value):
  if '.' in value:
   self._file_name = value
   print("Setting value of file_name to {}".format(value))
  else:
   print("Error: {} is not a valid file name".format(value))
def display_summary(self):
  data = pd.read_csv(self._path + self._file_name)
  print(self._file_name)
  print(data.info())

Поскольку мы определили все необходимые свойства и методы, продиктованные абстрактным классом выше, теперь мы можем создавать и использовать объекты производного подкласса CSVGetInfo.

Ключевые выводы

  • Абстрактные базовые классы отделяют интерфейс от реализации.
  • Они гарантируют, что производные классы реализуют методы и свойства, продиктованные абстрактным базовым классом.
  • Абстрактные базовые классы отделяют интерфейс от реализации. Они определяют общие методы и свойства, которые должны использоваться в подклассах. Реализация осуществляется конкретными подклассами, в которых мы можем создавать объекты, которые могут обрабатывать задачи.
  • Они помогают избежать ошибок и упрощают обслуживание иерархий классов, предоставляя строгий рецепт для создания подклассов.

Заключение

В этом посте я объяснил основы абстрактных базовых классов в Python.

Код в этом посте доступен в моем репозитории GitHub.

Надеюсь, этот пост был вам полезен.

Спасибо за чтение!