正确输入返回生成器表达式的可调用对象

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

我有以下代码片段

from contextlib import _GeneratorContextManager, contextmanager

GoodWrapperType = Callable[[int, str], _GeneratorContextManager[None]]
BadWrapperType = Callable[[int, str], Generator[None, None, None]]

def wrapper() -> GoodWrapperType:

    @contextmanager
    def inner(some_int: int, some_str: str) -> Generator[None, None, None]:
        # Do things with some_int, some_str
        yield

    return inner

我想在

pytest
测试套件的上下文中执行此操作,其中包装器将一些固定装置注入到
inner
中。

我上面有

GoodWrapperType
BadWrapperType
。 Pylance 告诉我,我无法将
BadWrapperType
指定为
wrapper
的返回类型。我已经找到了
GoodWrapperType
的解决方案,使用
_GeneratorContextManager
,但由于它带有
_
前缀,所以我不应该导入它。

有更好的方法吗?这样做的正确方法是什么?

我想知道没有直接的解决方案(无论如何我都找到了)这一事实是否可能暗示我不应该在Python中这样做。

python type-hinting pylance pyright
1个回答
0
投票

wrapper
不返回可返回生成器函数的可调用对象,它返回可返回上下文管理器的可调用对象,这是一个具有
__enter__
__exit__
方法的类。

@contextmanager
装饰器从它所应用的生成器函数构建此类,但最终结果不是生成器。

因此,我们希望输入

inner
作为返回生成器函数,但
wrapper
作为返回可调用函数,该可调用函数返回上下文管理器,该上下文管理器是应用装饰器的结果。

这是一个类型检查的版本:

from contextlib import contextmanager, AbstractContextManager
from typing import Callable, Iterator, ContextManager


WrapperType = Callable[[int, str], AbstractContextManager[None]]


def wrapper() -> WrapperType:

    @contextmanager
    def inner(some_int: int, some_str: str) -> Iterator[None]:
        # Do things with some_int, some_str
        yield

    return inner

https://mypy-play.net/?mypy=latest&python=3.11&flags=strict&gist=6b2b3360a4916ec654a84ac84fcea544

如果我们不使用生成器的发送和返回功能,我们可以将其类型简化为如上所述的

Iterator[<yield type>]

AbstractContextManager[<enter return type>]
是上下文管理器的通用类型。 (注意:Python 3.9 之前您应该使用
from typing import ContextManager
代替)

由于我们的内部函数没有产生任何结果,因此两种情况下的类型参数都是

None

但是

contextmanager
返回一个“上下文装饰器”,这是一个双重用途的类,可以充当上下文管理器或装饰器。使用
AbstractContextManager
键入将隐藏下游类型检查结果的装饰性。

如果您需要该属性,那么您最初使用

_GeneratorContextManager
的方法可能是最好的方法。 (有一个公共
contextlib.ContextDecorator
基类,但 mypy 缺乏交集类型,因此没有方便的方法仅使用公开导出的类型将某些内容同时键入
AbstractContextManager
ContextDecorator
)。

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