【问题标题】:Using an Argument to a Generic Type in Another Generic Type在另一个泛型类型中使用一个泛型类型的参数
【发布时间】:2021-10-31 12:52:27
【问题描述】:

标题可能有点模棱两可,因为我不确定如何准确表达我想要做的事情。

我想在 python 中从 C++ 复制类似这种模式:

template<typename T>
struct Foo
{
    using t = T;  // Alias T so it can be used in Bar
};


template<typename T>
struct Bar
{
    // The type T from Foo<T> can be used as a return type here
    typename T::t fn()
    {
        // do something that returns T::t
    }
};

int main(){
    // x is determined to be a float
    auto x = Bar<Foo<float>>().fn();
};

在 python 方面,我想将泛型类型 Bar[T] 特化为另一个特化类型 Foo[T],然后使用用于特化 Foo 的类型在 Bar 中键入提示。

类似

from typing import TypeVar, Generic

T = TypeVar("T")

class Foo(Generic[T]):
    ...

class Bar(Generic[Foo[T]]): # <-- This is illegal
    def fn(self) -> T:
        ...

# In the following, I would like the type checker to know that x is an int.
x = Bar[Foo[int]]().fn()

如果我们有类似的东西,我知道在创建 Bar 的实例时可以推断出这种关系

class Bar(Generic[T]):
    def __init__(self, foo: Foo[T]):
        ...

但这并不真正适合我目前的问题。

我希望能够创建一系列专业化Foo[T]Bar[T]Baz[T] 等,而无需多次重复T。在我的实际用例中,这些类型更像Foo[R, S, T],并且重复这些类型非常繁琐且容易出错,并且从概念上讲,类BarBaz 等被认为不依赖于T 类型,而是而是一种特殊类型的Foo[T]

所以如果有能力做类似的事情就好了

MyFoo = Foo[int]
MyBar = Bar[MyFoo]
MyBaz = Baz[MyFoo]

【问题讨论】:

  • 可以尝试使用typing.Union,如Union[int, str]
  • 不幸的是,我认为 Python 当前的打字语法不可能做到这一点。

标签: python type-hinting python-typing


【解决方案1】:

阅读 mypy 文档,我发现了这个:Advanced uses of self-types

通过在Bar 中使用以Foo 为界的TypeVar,我们可以指定Bar 由类似Foo 的东西参数化。然后,在单个方法中,我们可以使用嵌套类型 Bar[Foo[T]] 注释 self,导致 T 绑定到参数化 Foo 参数化 Bar 的参数。

from __future__ import annotations
from typing import TypeVar, Generic

T = TypeVar("T")

class Foo(Generic[T]):
    ...

# You may want to make `S` covariant with `covariant=True` 
# if you plan on using subclasses of `Foo` as parameters to `Bar`.
S = TypeVar("S", bound=Foo)

class Bar(Generic[S]):
    def fn(self: Bar[Foo[T]]) -> T:
        ...

# Below, when binding `fn`, `self` is recognized as 
# being of type `Bar[Foo[int]]` which binds `T` as `int` 
# and hence the return type is also `int`.
x = Bar[Foo[int]]().fn()

我一直想不通的是如何使用Bar[Foo[T]] 中的参数T 来注释Bar 的属性。类似的东西

class Bar(Generic[Foo[T]]):  # Still invalid.
    item: T

窗台不起作用。

但是,这可以通过将属性包装在属性中来解决

class Bar(Generic[S]):
    _item: Any
    
    @property
    def item(self: Bar[Foo[T]]) -> T:
        return self._item

_item 的类型提示不精确为代价。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2014-08-02
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 2018-06-12
    相关资源
    最近更新 更多