我应该如何类型提示一个也可以是无限的整数变量?
How should I type-hint an integer variable that can also be infinite?
搜索此主题时,我发现了以下内容: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
class 可以通过子class 扩展。这种方法并非没有许多陷阱和挑战,例如需要处理各种 __dunder__
方法(即 __add__
、__mul__
、__eq__
和之类的,所有这些都应该进行测试)。在需要特定值的用例中,这将是不可接受的开销。在这种情况下,用 typing.cast
包装所需的值将能够更好地向类型提示系统指示特定值(即 inf = cast(int, math.inf)
)可以接受分配。
这种方法不正确的原因很简单:因为分配的值 looks/feels 与某个数字完全一样,所以您 API 的其他一些用户可能最终无意中将其用作 int
,然后当提供 math.inf
(或类似的变体)时,程序可能会在他们身上严重爆炸。
一个类比是这样的:鉴于列表中的项目是由正整数索引的,我们希望任何 return 某个项目的索引的函数都是某个正整数,因此我们可以直接使用它 (我知道 Python 中的情况并非如此,因为存在允许使用负索引值的语义,但假装我们目前正在使用 C)。说这个函数 return 匹配项目的第一次出现,但是如果有任何错误它 return 一些负数,这显然超出了某些项目索引的有效值范围。缺乏防范 returned 值的天真使用将不可避免地导致类型系统应该解决的问题。
本质上,创建替代值并将其标记为 int
将提供零值,并且由于不正确的使用而不可避免地允许程序显示意外和损坏的 API/behavior 被自动允许.
更不用说 infinity is not a number,因此没有 int
值可以恰当地表示它(假设 int
代表一些有限的数字)。
顺便说一句,查看 str.index
vs str.find
。其中之一具有绝对违反用户期望的 return 值(即超出正整数类型的边界;不会被告知 return 值可能对于上下文无效在编译时使用 at,会导致运行时随机出现潜在故障)。
用更正确的术语构建 question/answer:
考虑到问题实际上是关于在比率存在时分配某个整数,并且如果 none 存在一些代表特定用例的无限性的其他标记应该完成(它可能是一些内置的 -值如 NotImplemented
或 None
)。然而,由于这些标记也不是 int
值,这意味着 myvar
实际上需要一个包含这些标记的类型,并且需要一种方法来应用可以做正确事情的操作。
不幸的是,这在 Python 中不能以非常好的方式直接使用,但是在像 Haskell 这样的强静态类型语言中,更被接受的解决方案是使用 Maybe
type to define a number type that can accept infinity .请注意,虽然浮点无穷大也在那里可用,但它继承了浮点数的所有问题,这使得它成为一个站不住脚的解决方案(同样,不要为此使用 inf
)。
回到Python:取决于你真正想要的赋值的属性,它可以像创建一个class一样简单,构造函数可以接受一个int
或None
(或NotImplemented
),然后提供class的用户可以使用实际值的方法。 Python 不幸的是,不提供高级构造来使它变得优雅,因此您将不可避免地以代码管理它而告终,这些代码会散落一地,或者必须编写许多方法来按预期处理任何输入并生成以您的程序实际需要的特定方式要求输出。
不幸的是,类型提示实际上只是触及表面,只是掠过更高级语言在更基础的层面上提供和解决的问题。我想如果必须在 Python 中编程,那总比没有好。
搜索此主题时,我发现了以下内容: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
class 可以通过子class 扩展。这种方法并非没有许多陷阱和挑战,例如需要处理各种 __dunder__
方法(即 __add__
、__mul__
、__eq__
和之类的,所有这些都应该进行测试)。在需要特定值的用例中,这将是不可接受的开销。在这种情况下,用 typing.cast
包装所需的值将能够更好地向类型提示系统指示特定值(即 inf = cast(int, math.inf)
)可以接受分配。
这种方法不正确的原因很简单:因为分配的值 looks/feels 与某个数字完全一样,所以您 API 的其他一些用户可能最终无意中将其用作 int
,然后当提供 math.inf
(或类似的变体)时,程序可能会在他们身上严重爆炸。
一个类比是这样的:鉴于列表中的项目是由正整数索引的,我们希望任何 return 某个项目的索引的函数都是某个正整数,因此我们可以直接使用它 (我知道 Python 中的情况并非如此,因为存在允许使用负索引值的语义,但假装我们目前正在使用 C)。说这个函数 return 匹配项目的第一次出现,但是如果有任何错误它 return 一些负数,这显然超出了某些项目索引的有效值范围。缺乏防范 returned 值的天真使用将不可避免地导致类型系统应该解决的问题。
本质上,创建替代值并将其标记为 int
将提供零值,并且由于不正确的使用而不可避免地允许程序显示意外和损坏的 API/behavior 被自动允许.
更不用说 infinity is not a number,因此没有 int
值可以恰当地表示它(假设 int
代表一些有限的数字)。
顺便说一句,查看 str.index
vs str.find
。其中之一具有绝对违反用户期望的 return 值(即超出正整数类型的边界;不会被告知 return 值可能对于上下文无效在编译时使用 at,会导致运行时随机出现潜在故障)。
用更正确的术语构建 question/answer:
考虑到问题实际上是关于在比率存在时分配某个整数,并且如果 none 存在一些代表特定用例的无限性的其他标记应该完成(它可能是一些内置的 -值如 NotImplemented
或 None
)。然而,由于这些标记也不是 int
值,这意味着 myvar
实际上需要一个包含这些标记的类型,并且需要一种方法来应用可以做正确事情的操作。
不幸的是,这在 Python 中不能以非常好的方式直接使用,但是在像 Haskell 这样的强静态类型语言中,更被接受的解决方案是使用 Maybe
type to define a number type that can accept infinity .请注意,虽然浮点无穷大也在那里可用,但它继承了浮点数的所有问题,这使得它成为一个站不住脚的解决方案(同样,不要为此使用 inf
)。
回到Python:取决于你真正想要的赋值的属性,它可以像创建一个class一样简单,构造函数可以接受一个int
或None
(或NotImplemented
),然后提供class的用户可以使用实际值的方法。 Python 不幸的是,不提供高级构造来使它变得优雅,因此您将不可避免地以代码管理它而告终,这些代码会散落一地,或者必须编写许多方法来按预期处理任何输入并生成以您的程序实际需要的特定方式要求输出。
不幸的是,类型提示实际上只是触及表面,只是掠过更高级语言在更基础的层面上提供和解决的问题。我想如果必须在 Python 中编程,那总比没有好。