Python 中的受限泛型类型提示
Restricted generic type hint in Python
我想像这里一样创建一个工厂方法。
class A:
...
class B(A):
def f(self):
...
class C:
...
def factory(cls):
return cls()
但我想添加一些具有两个要求的类型提示:
- 只允许
A
的子 class 作为 factory
的参数。
- 传递
B
时,正确检测到factory(B)
是B
的实例,即。 factory(B).f()
是允许的,而 factory(A).f()
是不允许的。
尝试 1
使用 typing
模块中的 Type
。
from typing import Type
class A:
...
class B(A):
def f(self):
...
class C:
...
def factory(cls: Type[A]):
return cls()
factory(A) # Ok
factory(B) # Ok
factory(C) # Fail
factory(A).f() # Fail
factory(B).f() # Fail -- Wrong!
这个正确地检测到 C
不应该作为 factory
的参数传递。但是,类型检查器不允许 factory(B).f()
。
尝试 2
使用TypeVar
.
from typing import TypeVar, Type
T = TypeVar('T')
class A:
...
class B(A):
def f(self):
...
class C:
...
def factory(cls: Type[T]) -> T:
return cls()
factory(A) # Ok
factory(B) # Ok
factory(C) # Ok -- Wrong!
factory(A).f() # Fail
factory(B).f() # Ok
很好推断,factory(B).f()
可以,而factory(A).f()
则不行。但是,泛型没有限制,即。 factory(C) 也可以。
尝试 3
向 T
添加约束。
from typing import TypeVar, Type
class A:
...
class B(A):
def f(self):
...
class D(A):
...
class C:
...
T = TypeVar('T', A)
def factory(cls: Type[T]) -> T:
return cls()
factory(A) # Ok
factory(B) # Ok
factory(C) # Fail
factory(A).f() # Fail
factory(B).f() # Ok
这看起来很有希望,至少 PyCharm 可以正确处理所有情况。但对于实际使用来说,这个解决方案实际上是最糟糕的 - A single constraint is not allowed
错误出现,但尚不清楚原因。在 PEP 484 中,只有简短的一行 'There should be at least two constraints, if any; specifying a single constraint is disallowed.'
有什么好的解决办法吗?我只有添加 'dummy' class _A
,A
的空白子 class,并将其作为另一个约束来拥有这样的东西。
_A = NewType('_A', A)
T = TypeVar('T', A, _A)
def factory(cls: Type[T]) -> T:
return cls()
但我真的不认为这是一个好的解决方案。
提前致谢!
使用 TypeVar
和 bound
似乎可行:
from typing import Type, TypeVar
class A:
...
class B(A):
def f(self):
...
class C:
...
AnyA = TypeVar("AnyA", bound=A)
def factory(cls: Type[AnyA]) -> AnyA:
return cls()
factory(A).f() # error: "A" has no attribute "f"
factory(B).f() # ok!
factory(C) # error: Value of type variable "AnyA" of "factory" cannot be "C"
The documentation about TypeVar
说:
...a type variable may specify an upper bound using bound=<type>
. This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subclass of the boundary type
这看起来和你想要的完全一样,所以你可以写:
T = TypeVar('T', bound=A)
我想像这里一样创建一个工厂方法。
class A:
...
class B(A):
def f(self):
...
class C:
...
def factory(cls):
return cls()
但我想添加一些具有两个要求的类型提示:
- 只允许
A
的子 class 作为factory
的参数。 - 传递
B
时,正确检测到factory(B)
是B
的实例,即。factory(B).f()
是允许的,而factory(A).f()
是不允许的。
尝试 1
使用 typing
模块中的 Type
。
from typing import Type
class A:
...
class B(A):
def f(self):
...
class C:
...
def factory(cls: Type[A]):
return cls()
factory(A) # Ok
factory(B) # Ok
factory(C) # Fail
factory(A).f() # Fail
factory(B).f() # Fail -- Wrong!
这个正确地检测到 C
不应该作为 factory
的参数传递。但是,类型检查器不允许 factory(B).f()
。
尝试 2
使用TypeVar
.
from typing import TypeVar, Type
T = TypeVar('T')
class A:
...
class B(A):
def f(self):
...
class C:
...
def factory(cls: Type[T]) -> T:
return cls()
factory(A) # Ok
factory(B) # Ok
factory(C) # Ok -- Wrong!
factory(A).f() # Fail
factory(B).f() # Ok
很好推断,factory(B).f()
可以,而factory(A).f()
则不行。但是,泛型没有限制,即。 factory(C) 也可以。
尝试 3
向 T
添加约束。
from typing import TypeVar, Type
class A:
...
class B(A):
def f(self):
...
class D(A):
...
class C:
...
T = TypeVar('T', A)
def factory(cls: Type[T]) -> T:
return cls()
factory(A) # Ok
factory(B) # Ok
factory(C) # Fail
factory(A).f() # Fail
factory(B).f() # Ok
这看起来很有希望,至少 PyCharm 可以正确处理所有情况。但对于实际使用来说,这个解决方案实际上是最糟糕的 - A single constraint is not allowed
错误出现,但尚不清楚原因。在 PEP 484 中,只有简短的一行 'There should be at least two constraints, if any; specifying a single constraint is disallowed.'
有什么好的解决办法吗?我只有添加 'dummy' class _A
,A
的空白子 class,并将其作为另一个约束来拥有这样的东西。
_A = NewType('_A', A)
T = TypeVar('T', A, _A)
def factory(cls: Type[T]) -> T:
return cls()
但我真的不认为这是一个好的解决方案。
提前致谢!
使用 TypeVar
和 bound
似乎可行:
from typing import Type, TypeVar
class A:
...
class B(A):
def f(self):
...
class C:
...
AnyA = TypeVar("AnyA", bound=A)
def factory(cls: Type[AnyA]) -> AnyA:
return cls()
factory(A).f() # error: "A" has no attribute "f"
factory(B).f() # ok!
factory(C) # error: Value of type variable "AnyA" of "factory" cannot be "C"
The documentation about TypeVar
说:
...a type variable may specify an upper bound using
bound=<type>
. This means that an actual type substituted (explicitly or implicitly) for the type variable must be a subclass of the boundary type
这看起来和你想要的完全一样,所以你可以写:
T = TypeVar('T', bound=A)