How to type hint python magic __get__ method

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

假设我们有以下类:

class Foo:
   def __init__(self, method):
       self.method = method

   def __get__(self, instance, owner):
       if instance is None:
          return self
       return self.method(instance)


class Bar:
    @Foo
    def do_something(self) -> int:
        return 1


Bar().do_something  # is 1
Bar.do_something    # is Foo object

如何正确输入 hint

__get__
method
以便 Pylance 理解
Bar().do_something
do_something
的返回类型? (像标准
property

python type-hinting python-typing magic-methods python-descriptors
1个回答
1
投票

你需要重载

__get__
方法。

我自己不使用 VSCode,但我用 MyPy 测试了下面的代码,我希望 Pyright 也能正确推断类型。

蟒蛇
>=3.9

为了尽可能灵活,我建议使

Foo

方面通用
  1. 使用描述符/装饰器的类,
  2. 修饰方法的参数说明,以及
  3. 修饰方法的返回类型。
from collections.abc import Callable
from typing import Generic, TypeVar, Union, overload
from typing_extensions import Concatenate, ParamSpec, Self

T = TypeVar("T")    # class using the descriptor
P = ParamSpec("P")  # parameter specs of the decorated method
R = TypeVar("R")    # return value of the decorated method


class Foo(Generic[T, P, R]):
    method: Callable[Concatenate[T, P], R]

    def __init__(self, method: Callable[Concatenate[T, P], R]) -> None:
        self.method = method

    @overload
    def __get__(self, instance: T, owner: object) -> R: ...

    @overload
    def __get__(self, instance: None, owner: object) -> Self: ...

    def __get__(self, instance: Union[T, None], owner: object) -> Union[Self, R]:
        if instance is None:
            return self
        return self.method(instance)

演示:

from typing import TYPE_CHECKING


class Bar:
    @Foo
    def do_something(self) -> int:
        return 1


a = Bar().do_something
b = Bar.do_something

print(type(a), type(b))  # <class 'int'> <class '__main__.Foo'>
if TYPE_CHECKING:
    reveal_locals()

在上面运行 MyPy 会给出所需的输出:

note: Revealed local types are:
note:     a: builtins.int
note:     b: Foo[Bar, [], builtins.int]

注意:(感谢@SUTerliakov 指出其中的一些内容)

  • 如果您使用的是 Python
    >=3.10
    ,则可以直接从 Concatenate
     导入 
    ParamSpec
    typing
     并且可以使用 
    |
    -notation 而不是
    typing.Union
  • 如果您使用的是 Python
    >=3.11
    ,您也可以直接从 Self
     导入 
    typing
    ,这意味着您根本不需要 
    typing_extensions

蟒蛇
<3.9

如果没有

Concatenate
ParamSpec
Self
,我们仍然可以根据修饰方法的返回值使
Foo
泛型:

from __future__ import annotations
from collections.abc import Callable
from typing import Generic, TypeVar, Union, overload

R = TypeVar("R")    # return value of the decorated method


class Foo(Generic[R]):
    method: Callable[..., R]

    def __init__(self, method: Callable[..., R]) -> None:
        self.method = method

    @overload
    def __get__(self, instance: None, owner: object) -> Foo[R]: ...

    @overload
    def __get__(self, instance: object, owner: object) -> R: ...

    def __get__(self, instance: object, owner: object) -> Union[Foo[R], R]:
        if instance is None:
            return self
        return self.method(instance)

来自上面的相同演示脚本的 MyPy 输出:

note: Revealed local types are:
note:     a: builtins.int
note:     b: Foo[builtins.int]
© www.soinside.com 2019 - 2024. All rights reserved.