【问题标题】:How to annotate the type of arguments forwarded to another function?如何注释转发给另一个函数的参数类型?
【发布时间】:2022-02-25 15:31:06
【问题描述】:

假设我们有一个调用 open() 但参数固定的普通函数:

def open_for_writing(*args, **kwargs):
    kwargs['mode'] = 'w'
    return open(*args, **kwargs)

如果我现在尝试调用 open_for_writing(some_fake_arg = 123),没有类型检查器(例如 mypy)可以判断这是一个不正确的调用:它缺少必需的 file 参数,并且正在添加另一个不属于open签名。

如何告诉类型检查器*args**kwargs 必须是open 参数规范的子集?我意识到 Python 3.10 有新的 ParamSpec 类型,但它似乎不适用于这里,因为你无法获得像 open 这样的具体函数的 ParamSpec

【问题讨论】:

  • 你真的需要一个子集吗?您的示例将接受与 open 相同的参数,尽管承认默默地忽略了一个。
  • @MisterMiyagi 有点像。我从该线程中得到的主要收获是我可以分配给target.__annotations__,这是我没有意识到的。使用装饰器并不是唯一的方法。
  • 复制__annotations__ 仅用于运行时检查。重要的部分是装饰器本身的注解,它在函数之间复制static类型信息。

标签: python python-3.x mypy python-typing


【解决方案1】:

我认为开箱即用这是不可能的。但是,您可以编写一个装饰器,它将包含您要检查的参数的函数(在您的情况下为打开)作为输入并返回修饰的函数,即 open_for_writing 在您的情况下。这当然只适用于 python 3.10 或使用 typing_extensions,因为它使用了 ParamSpec

from typing import TypeVar, ParamSpec, Callable, Optional

T = TypeVar('T')
P = ParamSpec('P')


def take_annotation_from(this: Callable[P, Optional[T]]) -> Callable[[Callable], Callable[P, Optional[T]]]:
    def decorator(real_function: Callable) -> Callable[P, Optional[T]]:
        def new_function(*args: P.args, **kwargs: P.kwargs) -> Optional[T]:
            return real_function(*args, **kwargs)

        return new_function
    return decorator

@take_annotation_from(open)
def open_for_writing(*args, **kwargs):
    kwargs['mode'] = 'w'
    return open(*args, **kwargs)


open_for_writing(some_fake_arg=123)
open_for_writing(file='')

here 所示,mypy 现在抱怨得到一个未知参数。

【讨论】:

  • 由于您只是复制整个签名,因此无需将其签名分开。对整个可调用对象进行参数化应该做同样的事情并且更容易。请参阅建议的副本如何做到这一点。
  • 为什么要定义另一个嵌套函数“new_function”而不是直接返回“real_function”?
猜你喜欢
  • 1970-01-01
  • 2023-03-09
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多