Mypy 类型通过递归泛型类型缩小

问题描述 投票:0回答:1

假设我创建了一个泛型类,其对象仅包含一个值(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 代替,但它不可订阅。

有什么好的(或者,至少不是太难看)的方法吗?

python python-3.x python-typing strong-typing
1个回答
0
投票

问题在于静态类型检查器通常无法从赋值中推断出动态类型更改,例如:

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 进行演示 此处

© www.soinside.com 2019 - 2024. All rights reserved.