假设我创建了一个泛型类,其对象仅包含一个值(T 类型)。
T = TypeVar('T')
class Contains(Generic[T]):
val: T
def __init__(self, val: T):
self.val = val
注意 self.val 本身可以是一个 Contains 对象,因此递归结构是可能的。我想定义一个函数,将这样的结构简化为单个非 Contains 对象。
def flatten(x):
while isinstance(x, Contains):
x = x.val
return x
“展平”的类型签名应该是什么?
我尝试制作递归嵌套类型
Nested = T | Contains['Nested[T]']
但是它使类型检查器感到困惑,因为 T 也可以表示 Contains 对象。
def flatten(x: Nested[T]) -> T:
while isinstance(x, Contains):
reveal_type(x) # reveals Contains[Unknown] | Contains[Nested]
x = x.val
reveal_type(x) # reveals object* | Unknown
return x
另一种方法是创建一个单独的类
class Base(Generic[T]):
self.val: T
def __init__(self, val):
self.val = val
Nested = Base[T] | Contains['Nested[T]']
def flatten(x: Nested[T]) -> T:
while isinstance(x, Contains):
x = x.val
return x.val
这可行,但是每次都必须将参数包装在 Base 对象中,这很麻烦。此外,Base 与 Contains 具有相同的行为,同样的事情被写了两次!我尝试使用 NewType 代替,但它不可订阅。
有什么好的(或者,至少不是太难看)的方法吗?
问题在于静态类型检查器通常无法从赋值中推断出动态类型更改,例如:
x = x.val
作为解决方法,您可以将
flatten
设置为递归函数,以避免分配给 x
:
from typing import TypeVar, Generic, TypeAlias
T = TypeVar('T')
class Contains(Generic[T]):
val: T
def __init__(self, val: T):
self.val = val
Nested: TypeAlias = T | Contains['Nested[T]']
def flatten(x: Nested[T]) -> T:
if isinstance(x, Contains):
return flatten(x.val)
reveal_type(x)
return x
使用 PyRight 进行演示 此处