类方法返回实例的 MyPy 注释
MyPy annotation for classmethod returning instance
我应该如何将 @classmethod
注释为 returns cls
的实例?这是一个不好的例子:
class Foo(object):
def __init__(self, bar: str):
self.bar = bar
@classmethod
def with_stuff_appended(cls, bar: str) -> ???:
return cls(bar + "stuff")
This returns a Foo
但更准确地说 returns 调用 Foo
的哪个子类,所以用 -> "Foo"
注释不会够用了。
诀窍是明确地向 cls
参数添加注释,结合 TypeVar
,对于 generics, and Type
, to represent a class rather than the instance itself,像这样:
from typing import TypeVar, Type
# Create a generic variable that can be 'Parent', or any subclass.
T = TypeVar('T', bound='Parent')
class Parent:
def __init__(self, bar: str) -> None:
self.bar = bar
@classmethod
def with_stuff_appended(cls: Type[T], bar: str) -> T:
# We annotate 'cls' with a typevar so that we can
# type our return type more precisely
return cls(bar + "stuff")
class Child(Parent):
# If you're going to redefine __init__, make sure it
# has a signature that's compatible with the Parent's __init__,
# since mypy currently doesn't check for that.
def child_only(self) -> int:
return 3
# Mypy correctly infers that p is of type 'Parent',
# and c is of type 'Child'.
p = Parent.with_stuff_appended("10")
c = Child.with_stuff_appended("20")
# We can verify this ourself by using the special 'reveal_type'
# function. Be sure to delete these lines before running your
# code -- this function is something only mypy understands
# (it's meant to help with debugging your types).
reveal_type(p) # Revealed type is 'test.Parent*'
reveal_type(c) # Revealed type is 'test.Child*'
# So, these all typecheck
print(p.bar)
print(c.bar)
print(c.child_only())
一般情况下,cls
(和self
)可以不加注解,但如果需要引用具体的子类,可以加一个explicit annotation。请注意,此功能仍处于试验阶段,在某些情况下可能存在错误。您可能还需要使用从 Github 克隆的最新版本的 mypy,而不是 pypi 上可用的版本——我不记得该版本是否支持类方法的此功能。
只是为了完整性,在 Python 3.7 中,您可以通过在文件开头导入 from __future__ import annotations
来使用 PEP 563 中定义的 postponed evaluation of annotations
。
然后你的代码看起来像
from __future__ import annotations
class Foo(object):
def __init__(self, bar: str):
self.bar = bar
@classmethod
def with_stuff_appended(cls, bar: str) -> Foo:
return cls(bar + "stuff")
根据 docs,此导入将从 Python 3.11.
有效地自动开始
我应该如何将 @classmethod
注释为 returns cls
的实例?这是一个不好的例子:
class Foo(object):
def __init__(self, bar: str):
self.bar = bar
@classmethod
def with_stuff_appended(cls, bar: str) -> ???:
return cls(bar + "stuff")
This returns a Foo
但更准确地说 returns 调用 Foo
的哪个子类,所以用 -> "Foo"
注释不会够用了。
诀窍是明确地向 cls
参数添加注释,结合 TypeVar
,对于 generics, and Type
, to represent a class rather than the instance itself,像这样:
from typing import TypeVar, Type
# Create a generic variable that can be 'Parent', or any subclass.
T = TypeVar('T', bound='Parent')
class Parent:
def __init__(self, bar: str) -> None:
self.bar = bar
@classmethod
def with_stuff_appended(cls: Type[T], bar: str) -> T:
# We annotate 'cls' with a typevar so that we can
# type our return type more precisely
return cls(bar + "stuff")
class Child(Parent):
# If you're going to redefine __init__, make sure it
# has a signature that's compatible with the Parent's __init__,
# since mypy currently doesn't check for that.
def child_only(self) -> int:
return 3
# Mypy correctly infers that p is of type 'Parent',
# and c is of type 'Child'.
p = Parent.with_stuff_appended("10")
c = Child.with_stuff_appended("20")
# We can verify this ourself by using the special 'reveal_type'
# function. Be sure to delete these lines before running your
# code -- this function is something only mypy understands
# (it's meant to help with debugging your types).
reveal_type(p) # Revealed type is 'test.Parent*'
reveal_type(c) # Revealed type is 'test.Child*'
# So, these all typecheck
print(p.bar)
print(c.bar)
print(c.child_only())
一般情况下,cls
(和self
)可以不加注解,但如果需要引用具体的子类,可以加一个explicit annotation。请注意,此功能仍处于试验阶段,在某些情况下可能存在错误。您可能还需要使用从 Github 克隆的最新版本的 mypy,而不是 pypi 上可用的版本——我不记得该版本是否支持类方法的此功能。
只是为了完整性,在 Python 3.7 中,您可以通过在文件开头导入 from __future__ import annotations
来使用 PEP 563 中定义的 postponed evaluation of annotations
。
然后你的代码看起来像
from __future__ import annotations
class Foo(object):
def __init__(self, bar: str):
self.bar = bar
@classmethod
def with_stuff_appended(cls, bar: str) -> Foo:
return cls(bar + "stuff")
根据 docs,此导入将从 Python 3.11.
有效地自动开始