Python 3.10 introduced the Foo | None
alternative type annotation syntax for Optional[Foo]
. This doesn’t seem to work when using forward reference to the enclosing type, like 'Foo' | None
, which instead results in a runtime error:
class Foo:
@staticmethod
def maybe_create(arg: str) -> 'Foo' | None:
if len(arg) < 5:
return Foo()
else:
return None
if __name__ == '__main__':
import sys
print(Foo.maybe_create(sys.argv[1]))
Traceback (most recent call last):
File "staticmethod-test.py", line 1, in <module>
class Foo:
File "staticmethod-test.py", line 3, in Foo
def maybe_create(arg: str) -> 'Foo' | None:
~~~~~~^~~~~~
TypeError: unsupported operand type(s) for |: 'str' and 'NoneType'
Using typing.Optional
works as expected, like:
from typing import Optional
class Foo:
@staticmethod
def maybe_create(arg: str) -> Optional['Foo']:
if len(arg) < 5:
return Foo()
else:
return None
if __name__ == '__main__':
import sys
print(Foo.maybe_create(sys.argv[1]))
Is there a way to express this optionality without extra imports?
>Solution :
Yes. If the union type contains forward references you have to wrap the entire return type in quotes.
Quoting from the documentation,
The
|
operand cannot be used at runtime to define unions where one or
more members is a forward reference. For example,int | "Foo"
, where
"Foo
" is a reference to a class not yet defined, will fail at runtime.
For unions which include forward references, present the whole
expression as a string, e.g."int | Foo"
.
class Foo:
@staticmethod
def maybe_create(arg: str) -> "Foo | None":
if len(arg) < 5:
return Foo()
else:
return None