[英]What's the idiomatic way to increment a Python new integer type?
Let's say I define a new type as such:假设我这样定义了一个新类型:
import typing
Index = typing.NewType('Index', int)
Then let's say I have an Index
variable as such:然后假设我有一个这样的Index
变量:
index = Index(0)
What would be the idiomatic way to increment index
?增加index
的惯用方法是什么?
If I do如果我做
index += 1
which is equivalent to这相当于
index = index + 1
then the type of index
becomes int
instead of Index
from a static type checking point of view:然后从 static 类型检查的角度来看, index
的类型变为int
而不是Index
:
$ mypy example.py
example.py:4: error: Incompatible types in assignment (expression has type "int", variable has type "Index")
Is there anything better than有什么比
index = Index(index + 1)
to keep the Index
type?保持Index
类型?
You have to create an int
subclass "for real" (pun not intended, but it is bad enough to stay there).您必须“真正地”创建一个int
子类(双关语不是故意的,但留在那里已经够糟糕了)。
There are two problems there:那里有两个问题:
See "typing.Newtype" at work in the interactive prompt, rather than relying on the static-check report:在交互式提示中查看“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
the proper way, the operations that result from applying any operator will still cast the result type back to int
, and won't be of the created subclass:第二个问题是,即使您以正确的方式子类化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
So, the only way out is to actually wrap all the magic methods which perform numeric operations in functions that "cast" the result back to the subclass.因此,唯一的出路是实际上将所有执行数字运算的魔术方法包装在将结果“转换”回子类的函数中。 One can avoid repeating the same pattern several times in the class body by creating a decorator to perform this casting, and appling it to all numeric methods in a for
loop in the class body itself.通过创建一个装饰器来执行此转换,并将其应用于 class 主体本身的for
循环中的所有数字方法,可以避免在 class 主体中多次重复相同的模式。
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
This will actually work.这实际上会起作用。
However, if the intent is that static type checking "sees" this wrapping taking place, you are off-the trail once more.但是,如果意图是 static 类型检查“看到”这种包装正在发生,那么您将再次偏离轨道。 Static type checkers won't understand method creation by applying a decorator in a loop inside a class body. Static 类型检查器无法通过在 class 主体内的循环中应用装饰器来理解方法创建。
In other words, I see no way out of this but by copy-and-pasting the casting applyed automatically in the example above in all relevant numeric methods, and then, create annotations while at it:换句话说,我认为没有办法解决这个问题,而是通过复制并粘贴在所有相关数字方法中在上面的示例中自动应用的转换,然后在其中创建注释:
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
I think that you will not be able to implicitly(indirectly) keep the type as a result of an operation on a NewType
variable, because as the documentation says:我认为由于对NewType
变量的操作,您将无法隐式(间接)保留类型,因为正如文档所述:
You may still perform all int operations on a variable of type
UserId
, but the result will always be of typeint
您仍然可以对UserId
类型的变量执行所有 int 操作,但结果将始终为int
类型
Thus, any operation on a variable will produce a result of the base type.因此,对变量的任何操作都会产生基本类型的结果。 If you want the result to be a NewType
, you need to specify it explicitly , eg as index = Index(index + 1)
.如果您希望结果为NewType
,则需要明确指定它,例如index = Index(index + 1)
。 Here Index
works as cast function.在这里, Index
作为演员 function 工作。
This is consistent with the purpose of NewType
:这与NewType
的目的一致:
The static type checker will treat the new type as if it were a subclass of the original type. static 类型检查器会将新类型视为原始类型的子类。 This is useful in helping catch logical errors这对于帮助捕获逻辑错误很有用
The intended purpose of NewType is to help you detect cases where you accidentally mixed together the old base type and the new derived type. NewType 的预期目的是帮助您检测意外将旧的基本类型和新的派生类型混合在一起的情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.