Доступ к protected атрибутам наследуемого класса
У меня есть абстрактный класс SCConnection который наследуется в двух других классах. Приведу два простых примера
class SCConnection(ABC):
__slots__ = (
'_ip',
'_port',
'_timeout',
'__connection',
)
def __init__(self, ip: str, port: int, timeout: int):
self._ip = ip
self._port = port
self._timeout = timeout
self.__connection = None
@property
def attributes(self) -> Tuple:
return self._ip, self._port, self._timeout
@abstractmethod
def open_connection(self):
raise NotImplementedError
def set_connection(self, connection_object):
if self.__connection is None:
self.__connection = connection_object
Собственно у классов разница только в типе подключения. В одном случае это Telnet, в другом SSL. Это происходит в методе open_connection
class FirstSystem(SCConnection):
def __init__(ip: str, port: int, timeout: int):
super(FirstSystem, self).__init__(
ip=ip,
port=port,
timeout=timeout
)
def open_connection(self):
ip, port, timeout = self.attributes
telnet_client = Telnet(ip, port, timeout)
self.set_connection(telnet_client)
class SecondSystem(SCConnection):
def __init__(self, ip: str, timeout: int, port: int = 31114):
super(SecondSystem, self).__init__(
ip=ip, port=port, timeout=timeout
)
def open_connection(self):
ip, port, timeout = self.attributes
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.load_verify_locations(cafile=certifi.where())
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
_connection = ssl_context.wrap_socket(_socket)
_connection.connect((ip, port))
_connection.settimeout(timeout)
self.set_connection(_connection)
В связи с этим вопрос, корректно ли так делать или лучше как-то по другому?
Ответы (1 шт):
Автор решения: insolor
→ Ссылка
Я считаю, что решение со свойством, которое возвращает кортеж из атрибутов, не очень удачное, вот почему:
- Если добавится новый атрибут, придется править во всех местах, где используется это свойство
- Не очевидно, в каком порядке будут возвращены значения атрибутов, можно перепутать
- Нельзя определить типы значений внутри кортежа, IDE вас не предупредит, если вы со значениями что-то не то будете пытаться сделать.
Варианты решения:
- Сделать отдельные property для каждого параметра. Минус - придется вручную прописывать эти property.
- Сделать контейнер для набора параметров, например объект или namedtuple, из него получать значения по именам, пример:
from abc import ABC, abstractmethod
from collections import namedtuple
ConnectionParameters = namedtuple("ConnectionParameters", "ip,port,timeout")
class SCConnection(ABC):
__slots__ = (
'__connection_parameters'
'__connection',
)
def __init__(self, ip: str, port: int, timeout: int):
self.__connection_parameters = ConnectionParameters(ip, port, timeout)
self.__connection = None
@property
def connection_parameters(self) -> ConnectionParameters:
return self.__connection_parameters
@abstractmethod
def open_connection(self):
raise NotImplementedError
@property
def connection(self):
return self.__connection
@connection.setter
def connection(self, connection_object):
if self.__connection is None: # Тут лучше бы бросать исключение, а не молча игнорировать попытку перезаписи
self.__connection = connection_object
class FirstSystem(SCConnection):
def __init__(ip: str, port: int, timeout: int):
super().__init__(
ip=ip,
port=port,
timeout=timeout
)
def open_connection(self):
params = self.connection_parameters
telnet_client = Telnet(params.ip, params.port, params.timeout)
self.connection = telnet_client
class SecondSystem(SCConnection):
def __init__(self, ip: str, timeout: int, port: int = 31114):
super().__init__(
ip=ip, port=port, timeout=timeout
)
def open_connection(self):
params = self.connection_parameters
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2)
ssl_context.load_verify_locations(cafile=certifi.where())
_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
connection = ssl_context.wrap_socket(_socket)
connection.connect((params.ip, params.port))
connection.settimeout(params.timeout)
self.connection = connection
Для namedtuple, конечно, не декларируются типы значений полей, так что, если это критично, то нужно переделать на объект с немодифицируемыми полями, но все остальное останется без изменений.