gpt4 book ai didi

python - 从CPython文档澄清 “it should be possible to change the value of 1”

转载 作者:行者123 更新时间:2023-12-03 07:35:12 26 4
gpt4 key购买 nike

看到这个链接:https://docs.python.org/3/c-api/long.html#c.PyLong_FromLong

The current implementation keeps an array of integer objects for all integers between -5 and 256; when you create an int in that range, you actually just get back a reference to the existing object. So, it should be possible to change the value of 1. I suspect the behavior of Python, in this case, is undefined. :-)



在这种情况下, 粗体线是什么意思?

最佳答案

这意味着Python中的整数是带有“值”字段的实际对象,用于保存整数的值。在Java中,您可以像这样表示Python的整数(当然,省略了很多细节):

class PyInteger {

private int value;

public PyInteger(int val) {
this.value = val;
}

public PyInteger __add__(PyInteger other) {
return new PyInteger(this.value + other.value);
}
}

为了不使周围具有相同值的Python整数百变,它沿以下方向缓存一些整数:

PyInteger[] cache = {
new PyInteger(0),
new PyInteger(1),
new PyInteger(2),
...
}

但是,如果您执行以下操作会发生什么(让我们暂时忽略 value是私有(private)的):

PyInteger one = cache[1];  // the PyInteger representing 1
one.value = 3;

突然,每次在程序中使用 1时,实际上都会得到 3,因为表示 1的对象的有效值为 3

确实,您可以在Python中做到这一点!也就是说:可以在Python中更改整数的有效数字值。 this reddit post中有一个答案。我为了完整起见在这里复制了它(原始版权归Veedrac所有):

import ctypes

def deref(addr, typ):
return ctypes.cast(addr, ctypes.POINTER(typ))

deref(id(29), ctypes.c_int)[6] = 100
#>>>

29
#>>> 100

29 ** 0.5
#>>> 10.0

Python规范本身并未说明如何在内部存储或表示整数。它也没有说应该缓存哪些整数,或者根本不缓存任何整数。简而言之:Python规范中没有任何内容定义如果您像这样;-)愚蠢的操作会发生什么。

我们甚至可以走得更远...

实际上,上面的 value字段实际上是一个整数数组,模拟一个任意的大整数值(对于64位整数,您只需组合两个32位字段,等等)。但是,当整数开始变大并超出标准的32位整数时,缓存不再是可行的选择。即使您使用字典,比较整数数组是否相等也将带来过多的开销,而获得的增益却很少。

您实际上可以使用 is比较身份来自己检查:

>>> 3 * 4 is 12
True
>>> 300 * 400 is 120000
False
>>> 300 * 400 == 120000
True

在典型的Python系统中,只有一个对象代表数字 12。另一方面, 120000几乎不会被缓存。因此,在上面, 300 * 400产生一个表示 120000的新对象,该对象不同于为右侧数字创建的对象。

为什么如此相关?如果您更改 129这样的小数字的值,它将影响所有使用该数字的计算。您很可能会严重破坏系统(直到重新启动)。但是,如果更改大整数的值,则影响将很小。

12的值更改为 13意味着 3 * 4将产生 13。将 120000的值更改为 130000的影响要小得多,并且 300 * 400仍会产生(新的) 120000而不是 130000

一旦将其他Python实现带入画面,事情就会变得更加难以预测。举例来说, MicroPython没有小数字对象,但可以动态地使它们富裕,而 PyPy可能只是将您的更改优化了。

底线:修改数字的确切行为确实是不确定的,但是取决于几个因素和确切的实现。

回答评论中的一个问题:上面的Veedrac代码中 6的意义是什么?

Python中的所有对象共享一个公共(public)的内存布局。第一个字段是引用计数器,它告诉您当前有多少其他对象引用该对象。第二个字段是对对象的类或类型的引用。由于整数没有固定的大小,因此第三个字段是数据部分的大小(您可以找到相关的定义 here (general objects)here (integers/longs)):
struct longObject {
native_int ref_counter; // offset: +0 / +0
PyObject* type; // offset: +1 / +2
native_int size; // offset: +2 / +4
unsigned short value[]; // offset: +3 / +6
}

在32位系统上, native_intPyObject*均占据32位,而在64位系统上,它们自然占据64位。因此,如果我们在64位系统上以32位(使用 ctypes.c_int)访问数据,则整数的实际值将在offset +6处找到。另一方面,如果将类型更改为 ctypes.c_long,则偏移量为 +3

因为CPython中的 id(x)返回 x的内存地址,所以您实际上可以自己检查一下。基于上面的 deref函数,我们来做:

>>> deref(id(29), ctypes.c_long)[3]
29
>>> deref(id(29), ctypes.c_long)[1]
10277248
>>> id(int) # memory address of class "int"
10277248

关于python - 从CPython文档澄清 “it should be possible to change the value of 1”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62188158/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com