Generate TypedDict from function's keyword arguments

半城伤御伤魂 提交于 2021-02-19 02:26:29

问题


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"

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

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:

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

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

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}')

The whole code would be:

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)

And running mypy would yield:

Success: no issues found in 1 source file

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

And running the code would output:

a : 1 , b : sabich

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] 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.



来源:https://stackoverflow.com/questions/63910610/generate-typeddict-from-functions-keyword-arguments

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!