Python 类型提示:Callable 后跟 TypeVar 是什么意思?

问题描述 投票:0回答:2

我试图理解以下代码中的类型提示

Getter[T]

简化示例

T = TypeVar('T')
Getter = Callable[[T, str], str]


class AbstractClass(abc.ABC):
    @abc.abstractmethod
    def extract(
        self,
        get_from_carrier: Getter[T],  #  <---- See here
        ...
    ) -> Context:

非常感谢您的帮助,因为我一直在为此烦恼。

原始源代码

原始源代码来自OpenTelemetry项目文件“textmap.py”

import abc
import typing

from opentelemetry.context.context import Context

TextMapPropagatorT = typing.TypeVar("TextMapPropagatorT")

Setter = typing.Callable[[TextMapPropagatorT, str, str], None]
Getter = typing.Callable[[TextMapPropagatorT, str], typing.List[str]]


class TextMapPropagator(abc.ABC):
    """This class provides an interface that enables extracting and injecting
    context into headers of HTTP requests. 
    ...
    """

    @abc.abstractmethod
    def extract(
        self,
        get_from_carrier: Getter[TextMapPropagatorT],
        carrier: TextMapPropagatorT,
        context: typing.Optional[Context] = None,
    ) -> Context:
python generics python-typing
2个回答
3
投票

可调用对象后跟类型变量意味着可调用对象是一个通用函数,它接受一个或多个通用类型的参数

T

类型变量

T
是任何泛型类型的参数。

线路:

Getter = Callable[[T, str], str]

Getter
定义为可调用函数的类型别名,该函数的参数为泛型类型
T
和字符串,返回类型为字符串。

因此,该行:

get_from_carrier: Getter[T]

定义一个参数 (

get_from_carrier
),它是一个通用函数。泛型函数的第一个参数是泛型类型
T

具体例子

通过看一个具体的例子可以更好地理解这一点。请参阅下面的

“instrumentation/opentelemetry-instrumentation-asgi/src/opentelemetry/instrumentation/asgi/
init.py”: 在调用

propagators.extract

中,函数

propagators.extract
是一个可调用函数,其第一个参数的类型为
get_header_from_scope
,而这个
dict
用作
dict
TextMapPropagatorT



0
投票
tl;dr:

def get_header_from_scope(scope: dict, header_name: str) -> typing.List[str]: """Retrieve a HTTP header value from the ASGI scope. Returns: A list with a single string with the header value if it exists, else an empty list. """ headers = scope.get("headers") return [ value.decode("utf8") for (key, value) in headers if key.decode("utf8") == header_name ] ... class OpenTelemetryMiddleware: """The ASGI application middleware. ... """ ... async def __call__(self, scope, receive, send): """The ASGI application ... """ if scope["type"] not in ("http", "websocket"): return await self.app(scope, receive, send) token = context.attach( propagators.extract(get_header_from_scope, scope) ) 是通用类型别名,相当于

_C[_T]

在这里,您将
Callable[[_T, int], int]

定义为

_C
的类型别名。当类型别名包含
Callable[[_T, int], int]
(在本例中为
TypeVar
)时,它就成为
泛型类型别名
。您可以像使用内置泛型类型(如 _T
List[T]
)一样使用它,例如,
Dict[K, V]
相当于
_C[str]
然后,

Callable[[str, int], int]

的类型注释将其定义为

泛型函数
。这意味着整个函数中使用的相同类型变量应该绑定到同一个类。要解释这意味着什么,请看一下这些函数调用: get_from_elem

在最后两种情况中,第一个参数是泛型函数,它不绑定类型变量,因此类型变量仅受第二个参数绑定。然而,在最后一种情况下,
_T = typing.TypeVar('_T') _C = typing.Callable[[_T,int],int] def get_from_elem(get: _C[_T], elem: _T): ... def foo_str(a: str, b: int) -> int: # This function matches `_C[str]`, i.e. `Callable[[str, int], int]` ... def foo_float(a: float, b: int) -> int: # This function matches `_C[float]`, i.e. `Callable[[float, int], int]` ... def foo_generic(a: _T, b: int) -> int: # This function matches `_C[_T]`, it is also a generic function ... _T2 = typing.TypeVar('_T2', str, bytes) def foo_str_like(a: _T2, b: int) -> int: # A generic function with constraints: type of first argument must be `str` or `bytes` ... get_from_elem(foo_str, "abc") # Correct: `_T` is bound to `str` get_from_elem(foo_float, 1.23) # Correct: `_T` is bound to `float` get_from_elem(foo_str, 1.23) # Wrong: `_T` bound to two different types `str` and `float` get_from_elem(foo_float, [1.23]) # Wrong: `_T` bound to two different types `float` and `List[float]` get_from_elem(foo_generic, 1.45) # Correct: `_T` is only bound to `float` get_from_elem(foo_str_like, 1.45) # Wrong: `_T` is only bound to `float`, but doesn't satisfy `foo_str_like` constraints

对其第一个参数类型有一个额外的约束,并且绑定类型

foo_str_like
不满足该约束,因此它无法进行类型检查。
    

© www.soinside.com 2019 - 2024. All rights reserved.