Python operator.index:需要整数个数时,为什么不该直接调用 int
个数接口不应悄悄替调用者取整
写一个取前几项的函数时,先做 n=int(value) 看起来很方便:字符串和浮点数都能变成整数。但传入三点九会直接变成三,错误的调用约定被隐藏了。如果接口要求调用者提供整数个数,更合适的入口是整数索引协议,再在得到整数后检查业务范围。
下面在 Python 3.12.14 中比较 int 与 operator.index。代码包含两个自定义对象,一个只声明能够转成 int,另一个声明自己支持整数索引。保存成 demo.py 后运行 python demo.py,所有拒绝路径都会被捕获并断言,不需要安装数值计算库。
协议通过与值合格要分成两步
ExactCount 的 __index__ 返回三,能作为切片边界;IntOnly 只有 __int__,所以 int 可以接受它,operator.index 却会拒绝。这里不是根据对象打印出来像不像数字做判断,而是看类型有没有提供适合整数场景的明确协议。
count_arg 先排除 bool,再调用 operator.index,最后限制范围为零到五。布尔值在 Python 中属于整数体系,仅靠整数协议不能排除 True。示例把这个业务约束单独写出来,也用负数和超大个数证明:支持协议并不自动意味着可以分配相应资源。
AI概念示意图:完整的整数形状通过对应入口,截掉一块的小数转换则代表另一种约定。图片只说明概念,不是运行截图。
import operator
class IntOnly:
def __int__(self):
return 3
class ExactCount:
def __index__(self):
return 3
def count_arg(value):
if isinstance(value, bool):
raise TypeError("boolean is not a count")
n = operator.index(value)
if not 0 <= n <= 5:
raise ValueError("count must be between 0 and 5")
return n
assert (int(3.9), int(-3.9)) == (3, -3)
assert int(IntOnly()) == 3
print("int conversion:", int(3.9), int(-3.9))
assert operator.index(ExactCount()) == 3
assert "ABCDE"[:ExactCount()] == "ABC"
print("integer protocol:", operator.index(ExactCount()), "ABCDE"[:ExactCount()])
rejected = []
for value in [3.0, 3.9, "3", IntOnly()]:
try:
operator.index(value)
except TypeError:
rejected.append(type(value).__name__)
else:
raise AssertionError("input must be rejected")
assert rejected == ["float", "float", "str", "IntOnly"]
print("rejected:", rejected)
for value, error in [(True, TypeError), (-1, ValueError), (6, ValueError)]:
try:
count_arg(value)
except error:
pass
else:
raise AssertionError("policy check must fail")
assert count_arg(3) == 3 and count_arg(ExactCount()) == 3
print("policy cases: 5 passed")观察转换造成的信息变化
int conversion 显示三和负三,因为普通浮点数转 int 会向零截断,而不是四舍五入。把这一步作为校验会丢掉小数部分,使调用方无法再知道原来传入的是三还是三点九。integer protocol 一行则确认支持 __index__ 的对象得到三,并能截取 ABC。
rejected 一行中,浮点数三点零、三点九、文本三以及只有 __int__ 的对象都得到 TypeError。即便三点零数学上没有小数部分,它仍没有因此成为支持整数索引协议的对象。是否允许这种输入,应由外层解析规则明确决定,不要偷偷交给 int 猜测。
policy cases 一行覆盖 True、负一和六,分别验证布尔值排除与范围校验。代码还确认普通整数三和 ExactCount 都通过,因此接口支持合格的整数类型,同时保住应用自己的边界。仅写 isinstance(value, int) 会拒绝某些支持整数协议的扩展类型。
文本解析仍有自己的入口
配置文件和命令行经常天然提供文本。如果设计允许文本数字,可以先按明确的文本格式规则解析,再传给内部整数接口;那是一项有意进行的格式转换。operator.index 本身不负责去空白、解释进制、接收小数或判断用户本来想输入什么。
__index__ 方法必须返回整数,也可能执行用户定义的代码或抛出异常,因此它不是运行不可信对象的沙箱。示例只构造了两个本地教学类,没有验证任意第三方类型的正确性。若输入来自反序列化边界,仍应先限定可接受的数据结构。
先识别布尔特例,再检查整数协议,最后校验业务范围,有助于错误提示说清楚原因。对于页数、批大小、重试次数或切片长度,特别要决定是否允许零以及最大值是多少。这样拒绝的是真正违背接口约定的输入,而不是把它悄悄变成另一项有效请求。


