Abstract Base Classes (ABC)#
Иногда, при создании иерархии классов, необходимо чтобы ряд классов поддерживал одинаковый интерфейс, например, одинаковый набор методов. Частично эту задачу можно решить с помощью наследования, однако далеко не всегда дочерним классам подойдет реализация метода из родительского класса.
Абстрактный класс - это класс в котором созданы абстрактные методы - методы, которые обязательно должны присутствовать в дочерних классах. Создавть экзепмляр абстрактного класса нельзя, его надо наследовать и уже у дочернего класса можно создать экземпляр. При этом экземпляр дочернего класса можно создать только в том случае, если у дочернего класса есть реализация всех абстрактных методов.
Базовый пример абстрактного класса:
In [1]: import abc
In [2]: class Parent(abc.ABC):
...: @abc.abstractmethod
...: def get_info(self, parameter):
...: """Get parameter info"""
...:
...: @abc.abstractmethod
...: def set_info(self, parameter, value):
...: """Set parameter to value"""
...:
Нельзя создать экземпляр класса Parent:
In [3]: p1 = Parent()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-0b1eb161869e> in <module>
----> 1 p1 = Parent()
TypeError: Can't instantiate abstract class Parent with abstract methods get_info, set_info
Дочерний класс обязательно должен добавить свою реализацию абстрактных методов, иначе при создании экземпляра возникнет исключение:
In [4]: class Child(Parent):
...: pass
...:
In [5]: c1 = Child()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-5-07f905b6a091> in <module>
----> 1 c1 = Child()
TypeError: Can't instantiate abstract class Child with abstract methods get_info, set_info
После создания методов get_info и set_info, можно создать экземпляр класса Child:
In [6]: class Child(Parent):
...: def __init__(self):
...: self._parameters = {}
...:
...: def get_info(self, parameter):
...: return self._parameters.get(parameter)
...:
...: def set_info(self, parameter, value):
...: self._parameters[parameter] = value
...: return self._parameters
...:
In [7]: c1 = Child()
In [8]: c1.set_info('name', 'BB-8')
Out[8]: {'name': 'BB-8'}
Пример абстрактного класса BaseSSH:
import paramiko
import time
import abc
class BaseSSH(abc.ABC):
def __init__(self, ip, username, password):
self.ip = ip
self.username = username
self.password = password
self._MAX_READ = 10000
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(
hostname=ip,
username=username,
password=password,
look_for_keys=False,
allow_agent=False)
self._ssh = client.invoke_shell()
time.sleep(1)
self._ssh.recv(self._MAX_READ)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self._ssh.close()
def close(self):
self._ssh.close()
@abc.abstractmethod
def send_command(self, command):
"""Send command and get command output"""
@abc.abstractmethod
def send_config_commands(self, commands):
"""Send configuration command(s)"""
Соответственно в дочерних классах обязательно должны быть методы send_command и send_config_commands:
class CiscoSSH(BaseSSH):
device_type = 'cisco_ios'
def __init__(self, ip, username, password, enable_password,
disable_paging=True):
super().__init__(ip, username, password)
self._ssh.send('enable\n')
self._ssh.send(enable_password + '\n')
if disable_paging:
self._ssh.send('terminal length 0\n')
time.sleep(1)
self._ssh.recv(self._MAX_READ)
def send_command(self, command):
self._ssh.send(command + '\n')
time.sleep(0.5)
result = self._ssh.recv(self._MAX_READ).decode('ascii')
return result
def config_mode(self):
self._ssh.send('conf t\n')
time.sleep(0.5)
result = self._ssh.recv(self._MAX_READ).decode('ascii')
return result
def exit_config_mode(self):
self._ssh.send('end\n')
time.sleep(0.5)
result = self._ssh.recv(self._MAX_READ).decode('ascii')
return result
def send_config_commands(self, commands):
result = self.config_mode()
result += super().send_config_commands(commands)
result += self.exit_config_mode()
return result