Предположим, у нас есть следующие классы:
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
Как правильно ввести подсказку __get__ и method, чтобы Pylance понял, что Bar().do_something имеет возвращаемый тип do_something? (как стандарт property)






Вам нужно перегрузить метод __get__.
Я сам не использую VSCode, но я протестировал приведенный ниже код с MyPy, и я ожидаю, что Pyright также правильно выведет типы.
>=3.9Чтобы сделать это максимально гибким, я бы предложил сделать Foo общим с точки зрения
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 за указание на это)
>=3.10, вы можете импортировать Concatenate и ParamSpec напрямую из typing и использовать |-нотацию вместо typing.Union.>=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]
@SUTerliakov Спасибо. Я отредактировал свой ответ. Ты прав. Достаточно различать <3.9 и >=3.9. (Я также неправильно использовал обозначение объединения | в старой версии.)
На самом деле нотация объединения труб также была в порядке, потому что у вас есть аннотации для импорта в будущее и использование трубы только в аннотациях:)
спасибо за развернутый ответ Даниил
@feiyang472 Рад, что смог помочь. Если это решило вашу проблему и дало удовлетворительный ответ на ваш вопрос, отметьте его как принятое, чтобы сообщить об этом другим пользователям.
Не могли бы вы добавить ссылку на
typing_extensions, чтобы прояснить, что требования к Python связаны только с возможностями модуля stltyping? Ваше первое решение будет отлично работать на py3.9 сtyping_extensionsимпортомSelf,ConcatenateиParamSpec.