Есть ли способ (или даже требование) в Python для обозначения абстрактных промежуточных классов? - PullRequest
0 голосов
/ 31 марта 2020

Рассмотрим следующий набор классов:

from abc import ABC, abstractmethod

class GrandParent(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()

    @abstractmethod
    def bar(self):
        raise NotImplementedError()


class Parent1(GrandParent):

    def foo(self):
        print("Parent 1")

class Parent2(GrandParent):

    def foo(self):
        print("Parent 2")

class Child1A(Parent1):
    def bar(self):
        print("Child 1A")

class Child1B(Parent1):
    def bar(self):
        print("Child 1B")

Здесь GrandParent, Parent1 и Parent2 являются абстрактными классами, а единственными конкретными являются Child1A и Child1B. Однако pylint жалуется на Parent s, что:

W0223: Method 'bar' is abstract in class 'GrandParent' but is not overridden (abstract-method)

Я могу понять причину, заключающуюся в том, что, взглянув на само определение класса, нельзя понять, что он должен быть абстрактным классом. Интересно, есть ли декоратор или что-то, что мне нужно использовать, чтобы явно указать это?

Одним из очевидных способов было бы переопределить абстрактный метод bar в Parent1 и Parent2, но это мне не кажется разумным (что, если в GrandParent есть 10 других абстрактных методов, которые дети должны переопределить ?, все 10 из них должны быть скопированы в Parent s?)


Обновление

В комментариях было высказано предположение, что это поведение pylint -specifi c, что промежуточные неявные ABC пока не поддерживаются. Чтобы избавиться от предупреждения (не отключая его), достаточно переопределить один из абстрактных методов GrandParent в Parent1 с использованием декоратора @abstractmethod. Действительно, это решает проблему, но вызовет проблемы в сценарии множественного наследования ios:

from abc import ABC, abstractmethod

class Base(ABC):
    @abstractmethod
    def foo(self):
        raise NotImplementedError()

    @abstractmethod
    def bar(self):
        raise NotImplementedError()

    def baz(self):
        return 'baz'

    def qux(self):
        return 'qux'

class C1(Base):

    def qux(self):
        return super().qux().upper()

    # @abstractmethod
    # def bar(self):
    #    raise NotImplementedError()


class C2(Base):
    def foo(self):
        return 'FOO'

    def bar(self):
        return 'BAR'


class D(C1, C2):
    pass

Здесь C1 является промежуточным неявным AB C, поэтому pylint предупреждает. Тем не менее, D - это конкретный класс. Чтобы избавиться от предупреждения pylint, мы можем раскомментировать переопределение bar в C1. Но тогда нельзя создать экземпляр D --- Python жалуется, что bar является абстрактным в C1 и не определен в D.

...