搜索这个主题我发现了以下内容:How to represent integer infinity?
我同意Martijn Peeters的观点,即为int
添加一个单独的特殊无限值可能不是最好的想法。
然而,这使得类型提示变得困难。假设以下代码:
myvar = 10 # type: int
myvar = math.inf # <-- raises a typing error because math.inf is a float
但是,代码在任何地方都应该按照应有的方式运行。我的类型提示在其他地方都是正确的。
如果我写下以下内容:
myvar = 10 # type: Union[int, float]
我可以毫不费力地分配math.inf
。但现在任何其他浮动也被接受。
有没有办法正确约束类型提示?或者每次指定无限时我都被迫使用type: ignore
?
int
类可以通过子类扩展而不是添加特定值。这种方法并非没有一些陷阱和挑战,例如需要处理各种__dunder__
方法的无穷大值(即__add__
,__mul__
,__eq__
等,所有这些都应该进行测试)。在需要特定值的用例中,这将是不可接受的开销量。在这种情况下,用typing.cast
包装所需值将能够更好地向类型提示系统指示特定值(即inf = cast(int, math.inf)
)是否可接受分配。
这种方法不正确的原因很简单:因为分配的值看起来/感觉完全像某个数字,API的其他一些用户可能最终无意中将其用作int
然后当math.inf
时程序可能会严重爆炸(或提供此类变体)。
一个类比是:假设列表中有正整数索引的项,我们可以期望任何返回某个项的索引的函数都是一个正整数,所以我们可以直接使用它(我知道这不是Python中的情况)鉴于存在允许使用负索引值的语义,但假装我们正在使用C语言。假设此函数返回匹配项的第一个匹配项,但如果有任何错误,则返回一些负数,这明显超出某个项的索引的有效值范围。缺乏防止返回值的天真使用将不可避免地导致类型系统应该解决的问题。
实质上,创建代理值并将其标记为int
将提供零值,并且不可避免地允许程序由于不正确的使用而允许出现意外和破坏的API /行为。
更不用说infinity is not a number这样的事实,因此没有int
值可以正确地表示(假设int
本质上代表一些有限数字)。
顺便说一句,看看str.index
和str.find
。其中一个具有肯定违反用户期望的返回值(即超出类型正整数的边界;不会被告知返回值对于在编译期间可能使用的上下文可能无效,导致在运行时随机发生潜在故障)。
鉴于问题实际上是在存在速率时分配一些整数,如果不存在,则应该完成一些代表特定用例无限的其他标记(它可能是一些内置值,如NotImplemented
或None
)。然而,由于这些标记也不是int
值,这意味着myvar
实际上需要一种包含这些标记的类型,并且采用一种方法来应用可以做正确事情的操作。
遗憾的是,Python不能直接以非常好的方式提供,但是在像Haskell这样的强静态类型语言中,更常用的解决方案是使用Maybe
类型来定义number type that can accept infinity。请注意,虽然浮点无穷大也可用,但它继承了浮点数的所有问题,这使得这是一个难以解决的解决方案(同样,不要使用inf
)。
回到Python:取决于你真正想要的赋值的属性,它可以像创建一个带有构造函数的类一样简单,该构造函数可以接受int
或None
(或NotImplemented
),然后提供一个方法,用户可以class可以利用实际值。遗憾的是,Python没有提供高级构造来使这个优雅,所以你将不可避免地最终得到管理这个遍布整个地方的代码,或者必须编写一些方法来处理任何预期的输入并产生所需的输出。您的计划实际需要的具体方式。
不幸的是,类型提示实际上只是在表面上,只是放弃了更高级的语言提供和解决的更基本的层面。我想如果一个人必须用Python编程,那就好比不用它。