您必须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']
# to get these, I did `print(dir(int))` copy-pasted the result, and
# deleted all non-relevant methods to this case, leaving only the ones
# that perform operations which should preserve the subclass
In [70]: class Index(int):
for meth in num_meths:
locals()[meth] = (lambda meth:
lambda self, *args:
__class__(getattr(int, meth)(self, *args))
)(meth)
# creating another "lambda" layer to pass the value of meth in _each_
# iteration of the for loop is needed so that these values are "frozen"
# for each created method
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))
# Rinse and repeat for all numeric methods you intend to use