get_type_hints 在 TypeVar 上
get_type_hints on TypeVar
给出这样的函数签名:
T = typing.TypeVar('T')
def foo(arg: T) -> '?':
return arg.op()
函数的return类型与arg类型的"op"方法的return类型相同。在我看来,这些信息应该可以通过 IDE.
推导出来
class SomeClass:
def op() -> int:
...
x = foo(SomeClass()) # IDE should know that x has type 'int'.
是否有类型提示的语法提示我如何从 foo
的函数签名中描述这种依赖关系?换句话说,我应该用什么表达式来替换上面代码中的 '?'
以获得预期的效果?
在这里使用 TypeVar 无效。因为你的 TypeVar 是不受约束的(没有声明自己受一种或多种类型的限制),你可以用它替换任何任意类型——object、int、str、float、Dict[str、List[str]]...
这意味着使用您当前的输入类型签名,return 类型是不可表达的:给定输入,有时根本就没有有效的 return 类型。
更广泛地说,您的工具是否能够推断出 return 类型实际上与此无关。 PEP 484 类型系统没有办法让你声明一个 return 类型,它完全基于函数体内的某些东西。相反,您必须声明 return 类型是固定的和具体的(如 int 或 str),或者是以某种方式与输入相关的通用类型。
后者有两种不同的方法。
首先,您可以坚持任何 class 与 op()
方法继承自某些 class,并让 class 输出 op
通用。例如:
from typing import Generic, TypeVar
TOp = TypeVar('TOp')
class Operation(Generic[TOp]):
def op(self) -> TOp: pass
class SomeClass(Operation[int]):
def op(self) -> int:
# ...snip...
return 4
class OtherClass(Operation[str]):
def op(self) -> str:
# ...snip...
return "output"
# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')
def foo(arg: Operation[T]) -> T:
return arg.op()
# Deduces the output types should be int and str respectively
a = foo(SomeClass())
b = foo(OtherClass())
反过来对 arg
施加这些约束让我们得出结论 return 类型必须是什么。
这种方法的唯一缺点是修改 SomeClass 和 OtherClass 可能有点麻烦。如果你不想这样做,你可以做的是创建一个自定义 Protocol:
# Or, if you need to use older versions of Python, pip-install typing-extensions
# and do 'from typing_extensions import Protocol'.
from typing import Generic, Protocol, TypeVar
TOp = TypeVar('TOp')
# Doing 'class Blah(Protocol[T])' is a shorthand for
# doing 'class Blah(Protocol, Generic[T])'.
class SupportsOp(Protocol[TOp]):
def op(self) -> TOp: ...
# SomeClass and OtherClass do *not* inherit SupportsOp!
class SomeClass:
def op(self) -> int:
# ...snip...
return 4
class OtherClass:
def op(self) -> str:
# ...snip...
return "output"
# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')
def foo(arg: SupportsOp[T]) -> T:
return arg.op()
# Still deduces the output types should be int and str respectively
a = foo(SomeClass())
b = foo(OtherClass())
主要区别在于协议使用结构子类型而不是名义子类型。对于名义子类型,如果 A 显式继承自 B,则类型 A 被认为是 B 的子类型。对于结构子类型,只要其方法与 B 中给出的方法签名匹配,A 就是 B 的子类型。
当然,您的 IDE 是否能够对此类泛型执行类型推断是一个单独的问题。像 mypy 这样的类型检查器肯定没有问题,而像 PyCharm 这样的 IDE 在大多数情况下通常似乎做得足够好,但你可能需要做一些测试才能知道什么是什么,什么不是'支持。
给出这样的函数签名:
T = typing.TypeVar('T')
def foo(arg: T) -> '?':
return arg.op()
函数的return类型与arg类型的"op"方法的return类型相同。在我看来,这些信息应该可以通过 IDE.
推导出来class SomeClass:
def op() -> int:
...
x = foo(SomeClass()) # IDE should know that x has type 'int'.
是否有类型提示的语法提示我如何从 foo
的函数签名中描述这种依赖关系?换句话说,我应该用什么表达式来替换上面代码中的 '?'
以获得预期的效果?
在这里使用 TypeVar 无效。因为你的 TypeVar 是不受约束的(没有声明自己受一种或多种类型的限制),你可以用它替换任何任意类型——object、int、str、float、Dict[str、List[str]]...
这意味着使用您当前的输入类型签名,return 类型是不可表达的:给定输入,有时根本就没有有效的 return 类型。
更广泛地说,您的工具是否能够推断出 return 类型实际上与此无关。 PEP 484 类型系统没有办法让你声明一个 return 类型,它完全基于函数体内的某些东西。相反,您必须声明 return 类型是固定的和具体的(如 int 或 str),或者是以某种方式与输入相关的通用类型。
后者有两种不同的方法。
首先,您可以坚持任何 class 与 op()
方法继承自某些 class,并让 class 输出 op
通用。例如:
from typing import Generic, TypeVar
TOp = TypeVar('TOp')
class Operation(Generic[TOp]):
def op(self) -> TOp: pass
class SomeClass(Operation[int]):
def op(self) -> int:
# ...snip...
return 4
class OtherClass(Operation[str]):
def op(self) -> str:
# ...snip...
return "output"
# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')
def foo(arg: Operation[T]) -> T:
return arg.op()
# Deduces the output types should be int and str respectively
a = foo(SomeClass())
b = foo(OtherClass())
反过来对 arg
施加这些约束让我们得出结论 return 类型必须是什么。
这种方法的唯一缺点是修改 SomeClass 和 OtherClass 可能有点麻烦。如果你不想这样做,你可以做的是创建一个自定义 Protocol:
# Or, if you need to use older versions of Python, pip-install typing-extensions
# and do 'from typing_extensions import Protocol'.
from typing import Generic, Protocol, TypeVar
TOp = TypeVar('TOp')
# Doing 'class Blah(Protocol[T])' is a shorthand for
# doing 'class Blah(Protocol, Generic[T])'.
class SupportsOp(Protocol[TOp]):
def op(self) -> TOp: ...
# SomeClass and OtherClass do *not* inherit SupportsOp!
class SomeClass:
def op(self) -> int:
# ...snip...
return 4
class OtherClass:
def op(self) -> str:
# ...snip...
return "output"
# Or alternatively, reuse the TOp variable from above -- it would mean the
# exact same thing, since TypeVars are only placeholders.
T = TypeVar('T')
def foo(arg: SupportsOp[T]) -> T:
return arg.op()
# Still deduces the output types should be int and str respectively
a = foo(SomeClass())
b = foo(OtherClass())
主要区别在于协议使用结构子类型而不是名义子类型。对于名义子类型,如果 A 显式继承自 B,则类型 A 被认为是 B 的子类型。对于结构子类型,只要其方法与 B 中给出的方法签名匹配,A 就是 B 的子类型。
当然,您的 IDE 是否能够对此类泛型执行类型推断是一个单独的问题。像 mypy 这样的类型检查器肯定没有问题,而像 PyCharm 这样的 IDE 在大多数情况下通常似乎做得足够好,但你可能需要做一些测试才能知道什么是什么,什么不是'支持。