你需要创建一个“真正的”
int
子类(这里没有双关语,但它足够糟糕,应该留在那里)。
这里有两个问题:
typing.NewType
并不会创建一个新类。它只是将对象的“血统”分离,以便它们会“看起来”像一个新类对于静态类型检查工具 - 但是使用这样的类创建的对象仍然是运行时指定的类。
请在交互式提示中查看“typing.Newtype”的工作情况,而不是依赖于静态检查报告:
In [31]: import typing
In [32]: Index = typing.NewType("Index", int)
In [33]: a = Index(5)
In [34]: type(a)
Out[34]: int
- 第二个问题是,即使您以正确的方式对
int
进行子类化,应用任何运算符产生的操作仍将将结果类型转换回int
,而不会是创建的子类:
In [35]: class Index(int): pass
In [36]: a = Index(5)
In [37]: type(a)
Out[37]: __main__.Index
In [38]: type(a + 1)
Out[38]: int
In [39]: type(a + a)
Out[39]: int
In [40]: a += 1
In [41]: type(a)
Out[41]: int
所以,唯一的解决方法是实际上将执行数值操作的所有魔术方法包装在“转换”结果回到子类的函数中。可以通过创建一个装饰器来执行此转换并将其应用于类主体中所有数字方法的for
循环中,以避免在类主体中多次重复相同的模式。
In [68]: num_meths = ['__abs__', '__add__', '__and__', '__ceil__',
'__divmod__', '__floor__', '__floordiv__', '__invert__', '__lshift__',
'__mod__', '__mul__', '__neg__', '__pos__', '__pow__', '__radd__',
'__rand__', '__rdivmod__', '__rfloordiv__', '__rlshift__', '__rmod__',
'__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__',
'__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__sub__',
'__truediv__', '__trunc__', '__xor__', 'from_bytes']
In [70]: class Index(int):
for meth in num_meths:
locals()[meth] = (lambda meth:
lambda self, *args:
__class__(getattr(int, meth)(self, *args))
)(meth)
In [71]: a = Index(5)
In [72]: type(a)
Out[72]: __main__.Index
In [73]: type(a + 1)
Out[73]: __main__.Index
In [74]: a += 1
In [75]: type(a)
Out[75]: __main__.Index
这实际上是可行的。
但是,如果意图是让静态类型检查“看到”这种包装的发生,那么你又偏离了轨道。 静态类型检查器不会理解在类体内循环应用装饰器创建方法。
换句话说,我认为唯一的出路就是将自动应用的转换复制并粘贴到所有相关的数字方法中,然后在此过程中创建注释:
from __future__ import annotations
from typing import Union
class Index(int):
def __add__(self: Index, other: Union[Index, int]) -> Index:
return __class__(super().__add__(other))
def __radd__(self: Index, other: Union[Index, int]) -> Index:
return __class__(super().__radd__(other))
__add__
和__iadd__
方法呢? - cs95mypy
将把它视为子类。 - juanpa.arrivillagaIndex(index+1)
或等效代码。 - juanpa.arrivillaga