Как включить проверку типов в абстрактный базовый класс в Python

Когда я определяю класс, мне нравится включать проверку типов (используя assert) входных переменных. Я сейчас определяю "специализированный" класс Rule который наследуется от абстрактного базового класса (ABC) BaseRule, аналогично следующему:

import abc

class BaseRule(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractproperty
    def resources(self):
        pass


class Rule(BaseRule):
    def __init__(self, resources):
        assert all(isinstance(resource, Resource) for resource in resources)    # type checking
        self._resources = resources

    @property
    def resources(self):
        return self._resources


class Resource(object):
    def __init__(self, domain):
        self.domain = domain


if __name__ == "__main__":
    resources = [Resource("facebook.com")]
    rule = Rule(resources)

assert заявление в __init__ функция Rule класс гарантирует, что resources вход представляет собой список (или другой повторяемый) из Resource объекты. Однако это также относится к другим классам, которые наследуются от BaseRuleпоэтому я хотел бы включить это утверждение в abstractproperty как-то. Как я могу пойти по этому поводу?

2 ответа

Решение

Сделайте так, чтобы ваш базовый класс имел неабстрактное свойство, которое вызывает отдельные абстрактные методы получения и установки. Свойство может выполнить необходимую проверку перед вызовом установщика. Другой код (такой как __init__ метод производного класса), который хочет запустить проверку, может сделать это, выполнив его присваивание через свойство:

class BaseRule(object):
    __metaclass__ = abc.ABCMeta

    @property
    def resources(self): # this property isn't abstract and shouldn't be overridden
        return self._get_resources()

    @resources.setter
    def resources(self, value):
        assert all(isinstance(resource, Resources) for resource in value)
        self._set_resources(value)

    @abstractmethod
    def _get_resources(self):   # these methods should be, instead
        pass

    @abstractmethod
    def _set_resources(self, value):
        pass

class Rule(BaseRule):
    def __init__(self, resources):
        self.resources = resources # assign via the property to get type-checking!

    def _get_resources(self):
        return self._resources

    def _set_resources(self, value):
        self._resources = value

Вы могли бы даже рассмотреть возможность перемещения __init__ метод из Rule в BaseRule класс, так как ему не нужно никаких знаний о RuleКонкретная реализация.

См. Эту документацию по аннотациям типа abc с помощью mypy-lang https://mypy.readthedocs.io/en/latest/class_basics.html

Другие вопросы по тегам