簡體   English   中英

Python 輸入:將 `**kwargs` 從 function 復制到另一個

[英]Python Typing: Copy `**kwargs` from on function to another

這是 Python 擴展函數中的常見模式,並使用**kwargs將所有關鍵字 arguments 傳遞給擴展的 function。

即采取

class A:
    def bar(self, *, a: int, b: str, c: float) -> str:
       return f"{a}_{b}_{c}"
   

class B:
    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):
    return f"NEW_{super().bar(**kwargs)}"

現在調用extension(no_existing="a")會導致TypeError ,這可以被 static 類型檢查器檢測到。

在運行代碼之前,如何注釋我的extension以檢測此問題?

此注釋也有助於 IDE 為我提供正確的extension建議。

PEP 612引入了ParamSpec (參見 文檔)類型。

我們可以利用它來生成一個裝飾器,它告訴我們的類型檢查器,裝飾函數與給定的 function 具有相同的 arguments:

from typing import Callable, ParamSpec, TypeVar, cast, Any, Type

# Our test function
def source_func(*, foo: str, bar: int) -> str:
    return f"{foo}_{bar}"

# Define some specification, see documentation
P = ParamSpec("P")
T = TypeVar("T")

# For a help about decorator with parameters see 
# https://stackoverflow.com/questions/5929107/decorators-with-parameters
def copy_kwargs(kwargs_call: Callable[P, Any], target: Type[T]) -> Callable[[Callable], Callable[P, T]]:
    """Decorator does nothing but returning the casted original function"""
    def return_func(func: Callable[..., T]) -> Callable[P, T]:
        return cast(Callable[P, T], func)

    return return_func


@copy_kwargs(source_func, float)
def kwargs_test(**kwargs) -> float:
    print(source_func(**kwargs))
    return 1.2

# define some expected return values
okay: float
broken_kwargs: float
broken_return: str

okay = kwargs_test(foo="a", bar=1)
broken_kwargs = kwargs_test(foo=1, bar="2")
broken_return = kwargs_test(foo="a", bar=1)

使用pyre檢查此文件會給出正確的警告:

ƛ Found 3 type errors!
src/kwargs.py:30:28 Incompatible parameter type [6]: In anonymous call, for 1st parameter `foo` expected `str` but got `int`.
src/kwargs.py:30:35 Incompatible parameter type [6]: In anonymous call, for 2nd parameter `bar` expected `int` but got `str`.
src/kwargs.py:31:0 Incompatible variable type [9]: broken_return is declared to have type `str` but is used as type `float`.

MyPy最近(2022 年 4 月 7 日)合並了我尚未檢查的 ParamSpec 的第一個實現

根據相關的 typedshed Issue,PyCharm 應該支持ParamSpec但沒有正確檢測到復制的**kwargs而是抱怨okay = kwargs_test(foo="a", bar=1)會無效 arguments。

相關問題:

基於@kound 的回答。

為了保持 DRY,我們可以在不重新聲明返回類型的情況下做同樣的事情。 稍后會推導類型變量T (不是在調用copy_kwargs時,而是在其返回的 function 時),但它不影響進一步的類型檢查。

from typing import Callable, ParamSpec, TypeVar, cast, Any

# Our test function
def source_func(*, foo: str, bar: int) -> str:
    return f"{foo}_{bar}"

# Define some specification, see documentation
P = ParamSpec("P")
T = TypeVar("T")

# For a help about decorator with parameters see 
# https://stackoverflow.com/questions/5929107/decorators-with-parameters
def copy_kwargs(kwargs_call: Callable[P, Any]) -> Callable[[Callable[..., T]], Callable[P, T]]:
    """Decorator does nothing but returning the casted original function"""
    def return_func(func: Callable[..., T]) -> Callable[P, T]:
        return cast(Callable[P, T], func)

    return return_func


@copy_kwargs(source_func)
def kwargs_test(**kwargs) -> float:
    print(source_func(**kwargs))
    return 1.2

reveal_type(kwargs_test(foo="a", bar=1))
reveal_type(kwargs_test(foo=1, bar="2"))

這是mypy playground 鏈接,可以實際查看它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM