PoB*_*lek 9 python type-hinting python-3.x
假设我有两个类,Base并且在中Child有一个工厂方法Base。factory方法调用另一个类方法,该类方法可以被Base的子类覆盖。
class Base(object):
@classmethod
def create(cls, *args: Tuple) -> 'Base':
value = cls._prepare(*args)
return cls(value)
@classmethod
def _prepare(cls, *args: Tuple) -> Any:
return args[0] if args else None
def __init__(self, value: Any) -> None:
self.value = value
class Child(Base):
@classmethod
def _prepare(cls, *args: Tuple) -> Any:
return args[1] if len(args) > 1 else None
def method_not_present_on_base(self) -> None:
pass
Run Code Online (Sandbox Code Playgroud)
有没有一种注释的方法,Base.create以便静态类型检查器可以推断Base.create()返回的实例Base和Child.create()返回的实例Child,以便下面的示例通过静态分析?
base = Base.create(1)
child = Child.create(2, 3)
child.method_not_present_on_base()
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,静态类型检查器会正确地抱怨类中method_not_present_on_base不存在Base。
我考虑过要Base变成一个通用类,并让子类将自己指定为类型参数,即将CRTP引入Python。
T = TypeVar('T')
class Base(Generic[T]):
@classmethod
def create(cls, *args: Tuple) -> T: ...
class Child(Base['Child']): ...
Run Code Online (Sandbox Code Playgroud)
但是对于来自C ++和所有其他语言的CRTP来说,这感觉有点不合逻辑
小智 10
Python 3.11 现在有一个 Self 类型,以防其他人也偶然发现这个老问题。版本 中添加了mypy支持,自 2023 年 2 月起在 PyPI 上可用。1.0
https://docs.python.org/3/library/typing.html#typing.Self
这是注释类方法工厂的返回值的一种 DRY 方式:
from typing import Self
from collections import defaultdict
class NestedDefaultDict(defaultdict):
def __init__(self, *args, **kwargs):
super().__init__(NestedDefaultDict, *args, **kwargs)
@classmethod
def from_nested_dict(cls, dict_) -> Self:
inst = NestedDefaultDict()
for key, val in dict_.items():
inst[key] = cls.from_nested_dict(val) if isinstance(val, dict) else val
return inst
Run Code Online (Sandbox Code Playgroud)
Self 也非常适合方法链接 API。我从 James Murphy 的视频中复制了下面的 Self-less 示例,并添加了 3 个注释。
# https://github.com/mCodingLLC/VideosSampleCode/blob/master/videos/095_method_chaining_and_self/method_chaining_and_self.py
class Player:
def __init__(self, name, position, fatigue=0):
self.name = name
self.position = position
self.fatigue = fatigue
def draw(self) -> Self:
print(f"drawing {self.name} to screen at {self.position}")
return self
def move(self, delta) -> Self:
self.position += delta
self.fatigue += 1
return self
def rest(self) -> Self:
self.fatigue = 0
return self
Run Code Online (Sandbox Code Playgroud)
确实有可能:此功能称为具有通用自定义的TypeVar(尽管这有点误导,因为在这种情况下,我们将其用于类方法)。我相信它的行为与您所链接的“ CRTP”技术大致相当(尽管我不是C ++专家,所以不能肯定地说)。
无论如何,您都可以这样声明基类和子类:
from typing import TypeVar, Type, Tuple
T = TypeVar('T', bound='Base')
class Base:
@classmethod
def create(cls: Type[T], *args: Tuple[Any]) -> T: ...
class Child(Base):
@classmethod
def create(cls, *args: Tuple[Any]) -> 'Child': ...
Run Code Online (Sandbox Code Playgroud)
注意:
cls子定义的注释。| 归档时间: |
|
| 查看次数: |
2118 次 |
| 最近记录: |