【问题标题】:Why doesn't small integer caching seem to work with int objects from the round() function in Python 3?为什么小整数缓存似乎不适用于 Python 3 中 round() 函数的 int 对象?
【发布时间】:2021-01-09 03:26:12
【问题描述】:

您能否解释一下为什么在 Python v3.8 中会发生这种情况?

a=round(2.3)
b=round(2.4)

print(a,b)
print(type(a),type(b))

print(a is b)
print(id(a))
print(id(b))

输出:

2 2
<class 'int'> <class 'int'>
False
2406701496848
2406701496656
>>>

2 在小整数缓存的范围内。那么为什么会有不同的对象具有相同的值呢?

【问题讨论】:

  • 小的 int 缓存不是保证,只是实现细节。从学术的角度来看,这个问题很有趣(我只是试图追踪它,它似乎应该从round 到float.__round__,它调用PyLong_FromDouble,对于小值将调用PyLong_FromLong,并且应该检查缓存,所以我不确定为什么它的行为不符合预期),但它是一个实现细节,而不是保证的细节,所以完全依赖它表明代码编写得很糟糕。
  • 在 Python 3.9 中无法重现... :-)(但在 3.8 中可以)
  • @superbrain:找到了变化。代码路径更改为将 C doubles 转换为 PyLongs 的优化,并且它在小的 int 缓存检查中循环了副作用。
  • @ShadowRanger 啊,当我第一次看到这种变化时,我以为他们只是将优化从 __round__ 实现转移到现在的位置。但再看一遍,我发现它在 __trunc__ 实现中。事实上,在 3.8 中,1.2.__round__() is 1 给了我False,而1.2.__trunc__() is 1 已经给了我True。

标签: python python-3.x integer


【解决方案1】:

看起来像在 3.8 中,PyLong_FromDouble(这是 float.__round__ 最终委托给的)explicitly allocates a new PyLong object and fills it in manually,没有对其进行规范化(通过 IS_SMALL_INT 检查和 get_small_int 缓存查找功能),所以它没有检查小的 int 缓存以解析为规范值。

这将在 3.9 中因issue 37986: Improve perfomance of PyLong_FromDouble() 而发生变化,现在当double 小到可以无损地表示为C long 时,它会委托给PyLong_FromLong。副作用是,这将使用小的 int 缓存,因为 PyLong_FromLong 可靠地标准化小值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-04
    • 2011-02-03
    • 2010-12-23
    • 2017-04-24
    • 2011-10-26
    • 1970-01-01
    相关资源
    最近更新 更多