Kou*_*und 6 python python-typing
这是 Python 扩展或包装函数中的常见模式,用于**kwargs将所有关键字参数传递给扩展函数。
即采取
class A:
def bar(self, *, a: int, b: str, c: float) -> str:
return f"{a}_{b}_{c}"
class B(A):
def bar(self, **kwargs):
return f"NEW_{super().bar(**kwargs)}"
def base_function(*, a: int, b: str, c: float) -> str:
return f"{a}_{b}_{c}"
def extension(**kwargs) -> str:
return f"NEW_{base_function(**kwargs)}"
Run Code Online (Sandbox Code Playgroud)
现在调用extension(not_existing="a")orB().bar(not_existing="a")会导致TypeError, 可以被静态类型检查器检测到。
如何在运行代码之前注释我的extension或B.bar以便检测到此问题?
此注释也有助于 IDE 为我提供有关extensionor 的正确建议B.bar。
Kou*_*und 13
我们可以利用它来生成一个装饰器,告诉我们的类型检查器,装饰函数与给定函数具有相同的参数:
\nfrom typing import (\n Callable, ParamSpec, TypeVar, cast, Any, Type, Literal,\n)\n\n# Define some specification, see documentation\nP = ParamSpec("P")\nT = TypeVar("T")\n\n# For a help about decorator with parameters see \n# https://stackoverflow.com/questions/5929107/decorators-with-parameters\ndef copy_kwargs(\n kwargs_call: Callable[P, Any]\n) -> Callable[[Callable[..., T]], Callable[P, T]]:\n """Decorator does nothing but returning the casted original function"""\n\n def return_func(func: Callable[..., T]) -> Callable[P, T]:\n return cast(Callable[P, T], func)\n\n return return_func\n\nRun Code Online (Sandbox Code Playgroud)\n这将定义一个装饰器,可用于将完整的ParameterSpec定义复制到我们的新函数,并保留它的返回值。
\n让我们测试一下(另请参阅MyPy Playground)
\n# Our test function for kwargs\ndef source_func(foo: str, bar: int, default: bool = True) -> str:\n if not default:\n return "Not Default!"\n return f"{foo}_{bar}"\n\n@copy_kwargs(source_func)\ndef kwargs_test(**kwargs) -> float:\n print(source_func(**kwargs))\n return 1.2\n\n# define some expected return values\nokay: float\nbroken_kwargs: float\nbroken_return: str\n\nokay = kwargs_test(foo="a", bar=1)\nbroken_kwargs = kwargs_test(foo=1, bar="2")\nbroken_return = kwargs_test(foo="a", bar=1)\nRun Code Online (Sandbox Code Playgroud)\n这与Pyre 1.1.310、mypy 1.2.0和 PyCharm 2023.1.1的预期一致。\n这三个版本都会抱怨损坏的 kwargs 和损坏的返回值。\n只有 PyCharm 无法检测参数,default因为PEP 612 支持是尚未完全实施。
我们仍然需要非常小心地应用这个函数。\n假设以下调用
\nruntime_error = kwargs_test("a", 1)\nRun Code Online (Sandbox Code Playgroud)\n将导致运行时错误 \xe2\x80\x9c kwargs_test1() 采用 0 个位置参数,但给出了 2 个\xe2\x80\x9d 而没有任何类型检查器抱怨。
\n因此,如果您像这样复制**kwargs,请确保将所有位置参数放入函数中。\n定义参数的函数应仅使用关键字参数。
因此,最佳实践source_func如下所示:
def source_func(*, foo: str, bar: int, default: bool = True) -> str:\n if not default:\n return "Not Default!"\n return f"{foo}_{bar}"\nRun Code Online (Sandbox Code Playgroud)\n但由于这可能经常用于库函数,因此我们并不总是能够控制source_func,因此请记住这个问题!
您还可以添加*args到目标函数来防止此问题:
def source_func(*, foo: str, bar: int, default: bool = True) -> str:\n if not default:\n return "Not Default!"\n return f"{foo}_{bar}"\nRun Code Online (Sandbox Code Playgroud)\nParamSpec创建此答案时,MyPy 和 PyCharm 使用时遇到问题。问题似乎已解决,但链接仍保留作为历史参考:
ParamSpec,但没有正确检测到复制的内容**kwargs,但抱怨okay = kwargs_test(foo="a", bar=1)参数无效。(现已修复)如果您想复制 kwargs 但又想允许其他参数,您需要将 kwargs 与Concanate结合使用:
\n# Our test function for args and kwargs\ndef source_func_a(\n a: Literal["a"], b: Literal["b"], c: Literal["c"], d: Literal["d"], default: bool =True\n) -> str:\n if not default:\n return "Not Default!"\n return f"{a}_{b}_{c};{d}"\n\n\n\n@copy_kwargs(source_func_a)\ndef args_test(a: Literal["a"], *args, c: Literal["c"], **kwargs) -> float:\n kwargs["c"] = c\n # Note the correct types of source_func are not checked for kwargs and args,\n # if args_test doesn\'t define them (at least for mypy)\n print(source_func(a, *args, **kwargs))\n return 1.2\n\n# define some expected return values\nokay_args: float\nokay_kwargs: float\nbroken_kwargs: float\nbroken_args: float\n\nokay_args = args_test("a", "b", "c", "d")\nokay_kwargs = args_test(a="a", b="b", c="c", d="d")\nborken_args = args_test("not", "not", "not", "not")\nbroken_kwargs = args_test(a="not", b="not", c="not", d="not")\nRun Code Online (Sandbox Code Playgroud)\n有关详细信息,请参阅MyPy Play。
\n注意:目前,您需要为要添加的每个变量定义装饰器,并且由于 Concanate 的性质,它们也可以作为参数添加在前面。
\n| 归档时间: |
|
| 查看次数: |
2732 次 |
| 最近记录: |