在搜索该主题时,我遇到了以下问题: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
?
答案 0 :(得分:1)
可以通过子类扩展int
类,而不必添加特定值。这种方法并非没有很多陷阱和挑战,例如要求处理各种__dunder__
方法(即__add__
,__mul__
,__eq__
和等等,所有这些都应进行测试)。在需要特定值的用例中,这将是不可接受的开销。在这种情况下,用typing.cast
包装所需的值将可以更好地向类型提示系统指示该特定值(即inf = cast(int, math.inf)
)可以接受分配。
此方法不正确的原因很简单:由于分配的值看起来/感觉完全像某个数字,因此您API的其他一些用户可能最终无意中将其用作int
,然后程序可能提供math.inf
(或此类变量)后,它们会爆炸得很厉害。
这是一个比喻:假设列表中有被正整数索引的项目,我们希望返回某个项目索引的任何函数都是某个正整数,因此我们可以直接使用它(我知道这不是给出了允许使用负索引值的语义,但假装我们暂时使用C语言)。假设此函数返回匹配项的第一个匹配项,但是如果有任何错误,它将返回负数,这显然超出了某个项的索引的有效值范围。缺乏对返回值的原始用法的防范将不可避免地导致类型系统应该解决的问题。
从本质上讲,创建替代值并将其标记为int
将提供零值,并且不可避免地会由于错误的用法而自动允许程序展示意外和损坏的API /行为。
更不用说infinity is not a number了,因此没有int
值可以正确地表示这一事实(假设int
就其本质而言代表某个有限数)。
顺便说一句,请查看str.index
和str.find
。其中之一的返回值肯定违反了用户的期望(即,超出了正整数类型的边界;不会被告知该返回值可能对于在编译时使用的上下文无效),从而导致在运行时随机发生潜在故障)。
给出的问题实际上是存在某个速率时分配一些整数,如果不存在,则应完成一些其他令牌来表示特定用例的无限制性(它可以是一些内置值,例如{{ 1}}或NotImplemented
)。但是,由于这些标记也将不是None
的值,这意味着int
实际上将需要一个包含那些标记的类型,并具有一种应用方法来执行正确的操作。
不幸的是,这种方法不能很好地直接在Python中使用,但是在像Haskell这样的强静态类型语言中,更受欢迎的解决方案是使用Maybe
类型来定义number type that can accept infinity。请注意,尽管那里也提供了浮点无穷大,但它继承了所有浮点数的问题,这些问题使解决方案变得站不住脚(同样,请勿为此使用myvar
)。
回到Python:根据您实际想要的赋值属性,它就像创建一个可以接受inf
或int
(或{{ 1}}),然后提供该类的用户可以使用实际值的方法。不幸的是,Python没有提供高级的结构来使它优雅,因此您将不可避免地以代码管理该代码为最终结果,或者不得不编写许多方法来处理期望的任何输入并在其中生成所需的输出。程序实际需求的具体方式。
不幸的是,类型提示实际上只是在摸索,只是在更基本的层次上浏览更高级的语言所提供和解决的内容。我以为如果必须使用Python编程,那比没有它要好。