如何在python中参数化通过@classmethod实例化的类的类型

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

我正在尝试定义一个通用基类,它可以像这样实例化自身的包装实例

from typing import Callable, TypeVar, Generic

T = TypeVar("T")
U = TypeVar("U")

class From(Generic[T]):
    def __init__(self, val: T):
        self.val = val
    
    @classmethod
    def unit(cls, val):
        return cls(val)
    
    def bind(self, func: Callable[[T], U]):
        return self.unit(func(self.val))

基本上,当使用更改类型的函数在

bind
实例上调用
From
时,例如

def to_string(i: int) -> str: 
    return str(i)

From(1).bind(to_string) # here, self.val = "1"

类型检查器会抱怨,因为

cls
中的
From.unit
已由
T
参数化(因此将
U
传递给
From[T]
会导致
U
不可分配给
T

您可以通过在

From
方法中返回
unit
的参数化实例来解决这个问题,如下所示:

class From(Generic[T]):
    ...
    @classmethod
    def unit(cls, val: U) -> "From[U]":
        return From[U](val)

这就像一个魅力。

但是,当我让另一个类继承

From
时,问题就出现了。我失去了一般性:

class Just(From[T]):
    pass

Just(1).bind(to_string) # equals From("1"), instead of Just("1")

不接受您应该在每个继承实例上定义具有相同形式和类名的新

unit
方法的解决方案,是否有某种方法可以重新参数化原始
cls
方法中的
unit
变量的类型?

我知道如果您在

cls.__orig_bases__
方法中打印它,您可以使用
(Generic[~T])
来查找
unit

所以我想也许你可以做类似的事情

setattr(cls, "__orig_bases__", (Generic[U],))

但这似乎毫无意义,以防类型检查器没有发现。

理想情况下

self
上应该有一些方法允许您访问未参数化的基类,这样您就可以
self.<get_baseclass>[U](<val>)
.

现在我的解决方案是在

# type: ignore
中添加
unit
,如下所示:

@classmethod
def unit(cls, val: U) -> "From[U]":
    """Return a new instance of the same encapsulating class, wrapping `val`"""
        return cls(val) # type: ignore

但这并不理想。

python python-typing monads
1个回答
0
投票

使用

Self
作为
unit
bind
的返回类型。

class From(Generic[T]):
    def __init__(self, val: T):
        self.val = val

    @classmethod
    def unit(cls, val) -> Self:
        return cls(val)

    def bind(self, func: Callable[[T], U]) -> Self:
        return self.unit(func(self.val))

现在

reveal_type(Just(1).bind(to_string))
根据需要返回
Just[int]

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