Repository navigation
Replies: 2 comments
|
The new syntax intentionally does not allow declaring variance, with the idea that it's better to infer the safe variance from code. Your own example is actually unsafe, and can be exploited to create unsound code: JelleZijlstra/unsoundness#55. You can get the new syntax to infer covariance by making the |
|
With Python 3.12+ generic syntax (PEP 695), variance is inferred from how the type parameter is used rather than explicitly declared. The issue in your example is that You can make covariance inference work by storing the value in a private attribute and exposing it through a read-only property: class Foo[T]:
def __init__(self, value: T) -> None:
self._value = value
@property
def value(self) -> T:
return self._value
class Bar[U]:
def __init__(self, value: U) -> None:
self._value = value
@property
def value(self) -> U:
return self._value
type Combined[T, U] = Foo[T] | Bar[U]
def func1() -> Combined[int, str]:
return Foo(1)
def func2() -> Combined[float, str]:
return func1()This allows mypy to infer covariance for both Alternatively, if you want to retain the public attribute without making it private, you can use from typing import Final
class Foo[T]:
def __init__(self, value: T) -> None:
self.value: Final = valueThis also allows covariance inference because the attribute cannot be reassigned through statically checked code. The key distinction is that the new generic syntax does not automatically make types covariant. It infers variance based on how the type parameters are used. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I have the following example, where I am intending that
Combined[T, U]and the underlyingFoo[T]andBar[U]are covariant in both T and U. This passes mypy's type checking and works as intended:With Python 3.12+, I believe I can rewrite the definition of
FoofromFoo(Generic[T])toFoo[T], and the same for Bar. However, when I do so, Mypy no longer considers the types to be covariant, and complains thatfunc1's return type is incompatible withfunc2's:Mypy's complaint being:
Do I need to take extra steps to describe the covariance? Checking the Mypy docs, I can find examples like the below stating that the new syntax implicitly assumed covariance, which suggest it should work as I expect:
(from https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generic-types)
Removing the
TypeVar()definitions which become unnecessary with the new syntax does not change the type checking behaviour.All reactions