从函数的关键字参数生成 TypedDict [英] Generate TypedDict from function's keyword arguments

查看:48
本文介绍了从函数的关键字参数生成 TypedDict的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

foo.py:

kwargs = {"a": 1, "b": "c"}

def consume(*, a: int, b: str) -> None:
    pass

consume(**kwargs)

mypy foo.py:

error: Argument 1 to "consume" has incompatible type "**Dict[str, object]"; expected "int"
error: Argument 1 to "consume" has incompatible type "**Dict[str, object]"; expected "str"

这是因为 objectintstr 的超类型,因此被推断出来.如果我声明:

This is because object is a supertype of int and str, and is therefore inferred. If I declare:

from typing import TypedDict

class KWArgs(TypedDict):
    a: int
    b: str

然后将 kwargs 注释为 KWArgsmypy 检查通过.这实现了类型安全,但需要我在 KWArgs 中复制 consume 的关键字参数名称和类型.有没有办法在类型检查时从函数签名中生成这个 TypedDict,这样我就可以最大限度地减少维护中的重复?

and then annotate kwargs as KWArgs, the mypy check passes. This achieves type safety, but requires me to duplicate the keyword argument names and types for consume in KWArgs. Is there a way to generate this TypedDict from the function signature at type checking time, such that I can minimize the duplication in maintenance?

推荐答案

据我所知,这个 [1] 没有直接的解决方法,但有另一种优雅的方式来实现正是这样:

To the best of my knowledge, there is no direct workaround on this [1], but there is another elegant way to achieve exactly that:

我们可以利用typingNamedTuple来创建一个保存参数的对象:

We can utilize the typings NamedTuple to create an object that holds the parameter:

ConsumeContext = NamedTuple('ConsumeContext', [('a', int), ('b', str)])

现在我们定义consume方法来接受它作为参数:

Now we define the consume method to accept it as a parameter:

def consume(*, consume_context : ConsumeContext) -> None:
    print(f'a : {consume_context.a} , b : {consume_context.b}')

整个代码将是:

from typing import NamedTuple

ConsumeContext = NamedTuple('ConsumeContext', [('a', int), ('b', str)])

def consume(*, consume_context : ConsumeContext) -> None:
    print(f'a : {consume_context.a} , b : {consume_context.b}')

ctx = ConsumeContext(a=1, b='sabich')

consume(consume_context=ctx)

并且运行 mypy 会产生:

And running mypy would yield:

Success: no issues found in 1 source file

它会识别 ab 是参数,并批准它.

It will recognize that a and b are parameters, and approve that.

运行代码会输出:

a : 1 , b : sabich

但是,如果我们把 b 改成不是字符串,mypy 就会报错:

However, if we change b to be not a string, mypy will complain:

foo.py:9: error: Argument "b" to "ConsumeContext" has incompatible type "int"; expected "str"
Found 1 error in 1 file (checked 1 source file)

通过这种方式,我们通过定义一次方法的参数和类型来实现对方法的类型检查.

By this, we achieve type checking for a method by defining once it's parameters and types.

[1] 因为如果定义 TypedDict 或函数签名,基于另一个,将需要知道另一个的 __annotations__,这是不知道的检查时,并定义一个装饰器来在运行时转换类型忽略了类型检查的要点.

[1] Because if either defining TypedDict or function signature, based on the other, would require to know the other's __annotations__, which is not known on check-time, and defining a decorator to cast types on run-time misses the point of type checking.

这篇关于从函数的关键字参数生成 TypedDict的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆