In the following code, I have two classes that contain methods that are supposed to return instances of the other, and without annotations works as expected:
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
However, adding type annotations to the code results in a NameError
for any annotations on Foo
indicating Bar
as the type, since Bar
is not yet declared:
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
In this case simply switching the order doesn't help either, since Bar
also needs to reference Foo
in its annotations.
How can I annotate functions with class that is not yet declared?
In other languages this would be handled with a forward declaration, however I'm not aware of such a feature in python. I've tried the "obvious" way to simulate a forward declaration (declaring an empty class), and while it appears to work on the surface, actually inspecting the annotations reveals that this approach is flawed:
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
Another possibility would be to annotate Foo
with a base class of Bar
, but this starts getting fairly messy:
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
How can I annotate functions with class that is not yet declared?