Как я могу аннотировать функции с типом класса, который еще не объявлен?

В следующем коде у меня есть два класса, которые содержат методы, которые должны возвращать экземпляры другого, и без аннотаций работают должным образом:

class Foo:
    def __init__(self, bar = None):
        self.bar = bar or Bar(self)
    def foo(self):
        return self.bar

class Bar:
    def __init__(self, foo = None):
        self.foo = foo or Foo(self)
    def bar(self):
        return self.foo

f = Foo() # verify everything works as intended
assert f.foo().bar() == f 

Однако добавление аннотаций типов в код приводит к NameError для любых аннотаций на Foo указывающий Bar как тип, так как Bar еще не объявлено:

from typing import *
class Foo:
    def __init__(self, bar : Optional[Bar] = None):
        self.bar = bar or Bar(self)
    def foo(self) -> Bar:
        return self.bar

class Bar:
    def __init__(self, foo : Optional[Foo] = None):
        self.foo = foo or Foo(self)
    def bar(self) -> Foo:
        return self.foo

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

Как я могу аннотировать функции с классом, который еще не объявлен?


На других языках это будет обрабатываться с помощью предварительной декларации, однако я не знаю о такой возможности в python. Я попробовал "очевидный" способ смоделировать предварительное объявление (объявление пустого класса), и, хотя он работает на поверхности, фактическая проверка аннотаций показывает, что этот подход имеет недостатки:

from typing import *
class Bar:
    pass

class Foo:
    def __init__(self, bar : Optional[Bar] = None):
        self.bar = Bar(self) if bar is None else bar
    def foo(self) -> Bar:
        return self.bar

class Bar:
    def __init__(self, foo : Optional[Foo] = None):
        self.foo = Foo(self) if foo is None else foo
    def bar(self) -> Foo:
        return self.foo

assert get_type_hints(Bar.bar)['return'] == Foo #correct

# but the original `Bar` used in the annotation is different
# from the redeclared one so this fails:
assert get_type_hints(Foo.foo)['return'] == Bar 

Другая возможность будет аннотировать Foo с базовым классом Bar, но это начинает становиться довольно грязным:

from typing import *
from abc import ABCMeta, abstractmethod
class Bar_Base(object, metaclass=ABCMeta):
    @abstractmethod
    def __init__(self):
        pass

class Foo:
    def __init__(self, bar : Optional[Bar_Base] = None):
        self.bar = Bar(self) if bar is None else bar
    def foo(self) -> Bar_Base:
        return self.bar

class Bar(Bar_Base):
    def __init__(self, foo : Optional[Foo] = None):
        super().__init__()
        self.foo = Foo(self) if foo is None else foo
    def bar(self) -> Foo:
        return self.foo

assert issubclass(Foo, get_type_hints(Bar.bar)['return']) #correct
assert issubclass(Bar, get_type_hints(Foo.foo)['return']) #correct

Как я могу аннотировать функции с классом, который еще не объявлен?

0 ответов

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