Python: как переопределить подсказку типа на атрибуте экземпляра в подклассе? - PullRequest
3 голосов
/ 25 сентября 2019

Перед тем, как вы погрузитесь, вот мой вопрос: как я могу использовать подсказки типов в подклассе для указания другого типа в атрибуте экземпляра?

Если вам неясно, что это значит, прочитайте ниже,где я нарисовал пример для разъяснения вещей.


Полное объяснение

У меня есть абстрактный класс Foo и подкласс Fooс именем SubclassOfFoo.

Foo имеет абстрактный метод get_something, который возвращает объект типа Something.

Something имеет подкласс с именем SubclassOfSomething.SubclassOfSomething имеет дополнительный метод something_special.

SubclassOfFoo переопределяет get_something для возврата объекта типа SubclassOfSomething.Затем SubclassOfFoo пытается использовать метод SubclassOfSomething something_special.

Однако в настоящее время проверки моего PyCharm сообщают Unresolved attribute reference 'something_special' for class 'Something'.Я пытаюсь найти правильный способ исправить это.

Это все очень запутанно, поэтому я сделал хороший небольшой фрагмент кода, чтобы помочь здесь:


from abc import ABC, abstractmethod


class Something:
    def __init__(self):
        self.attr = 0


class SubclassOfSomething(Something):
    def __init__(self):
        Something.__init__(self)

    def something_special(self):
        self.attr = 1


class Foo(ABC):
    def __init__(self):
        self.my_class = self.get_something()

    @abstractmethod
    def get_something(self) -> Something:
        pass


class SubclassOfFoo(Foo):
    def __init__(self):
        Foo.__init__(self)

    def get_something(self) -> SubclassOfSomething:
        return SubclassOfSomething()

    def do_something_special(self):
        self.my_class.something_special()

В основном, вЧтобы все получилось, я могу сделать одну из следующих вещей:

  1. Удалить подсказку типа при возврате get_something в пределах Foo
  2. Использовать подсказку типав SubclassOfFoo для self.my_class, чтобы прояснить ситуацию
  3. Использовать дженерики?

Вариант № 1 - это то, чего я пытаюсь избежать

Вариант № 2 - это вариантнеплохо, но я не могу понять это

Вариант №3 также является вариантом.

Я также открыт для других вариантов, так как я уверен, что есть лучший способ.

Не могли бы вы помочь мне выяснить, как правильно с этим справиться?


Что я уже пробовал

Чтобы эмулировать вариант №2, я попытался использовать typing.Type, как предлагается здесь: Подкласс в подсказке типа

Однако, это не сработало для меня.

Ответы [ 2 ]

2 голосов
/ 25 сентября 2019

Вы можете указать подсказку типа для атрибута my_class в начале определения класса:

class SubclassOfFoo(Foo):
    my_class: SubclassOfSomething  # <- here

    def get_something(self) -> SubclassOfSomething:
        return SubclassOfSomething()

    def do_something_special(self):
        self.my_class.something_special()

После этого нет предупреждения Unresolved attribute reference 'something_special' for class 'Something' из проверки PyCharm, потому что теперь my_class известнобыть SubclassOfSomething не Something.

2 голосов
/ 25 сентября 2019

Вы также можете указать метод something_special для Something и поднять NotImplementedError

class Something:
    def __init__(self):
        self.attr = 0

    def something_special(self):
        raise NotImplementedError()

Это решит проблему подсказок типа, хотя функционально вызовет исключение в той же точке.(если вам удалось как-то получить Something и попытаться позвонить something_special, просто будет NotImplementedError вместо AttributeError).

Возможно, в некоторых ситуациях вам может понадобиться просто passвместо этого, в зависимости от того, что на самом деле является something_special.

class Something:
    def __init__(self):
        self.attr = 0

    def validate(self):
        # doesn't want to perform validation
        pass


class SubclassOfSomething(Something):
    def __init__(self):
        Something.__init__(self)

    def validate(self):
        if self.attr < 0:
            raise ValueError()

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...