简体   繁体   English

增加 Python 新 integer 类型的惯用方法是什么?

[英]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:那里有两个问题:

  • typing.NewType does not create a new class. typing.NewType不会创建新的 class。 It just separate a "lineage" of objects so that they will "look like" a new class to static-type checking tools - but objects created with such a class are still of the indicated class in runtime.它只是将对象的“谱系”分开,以便它们在静态类型检查工具中“看起来像”一个新的 class - 但是使用这样的 class 创建的对象在运行时仍然是指示的 class。

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
  • The second problem, is that even if you subclass 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 type int您仍然可以对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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM