为什么不同数据类型的地址不同[Python]?

Why the addresses of different datatypes different [Python]?

>>> a=5
>>> b=6
>>> id(a)
10914496
>>> id(b)
10914528
>>> c='Hello'
>>> d='World'
>>> id(c)
139973573252184
>>> id(d)
139973616356744
>>> e=(4>5)
>>> f=(4<5)
>>> id(e)
10739968
>>> id(f)
10740000
  1. 为什么字符串的地址长度和boolean/int数据类型如此不同?
  2. 为什么后续声明的地址与数据类型的大小相差很大?

更新#1

>>> id(c)
139973616356856
>>> id(c[0])
139973652926112
>>> id(c[1])
139973653190728
>>> id(c[2])
139973653634272
>>> id(c[3])
139973653302104

我有这个疑问,因为我首先学习了 C++(老实说,是 Turbo C++),并且 Python 中定义字符串地址的方式与 C++ 中发生的方式非常不同。我猜这在 Python 中没问题,因为我们无法通过对象在 Python 中的地址访问它,对吗?

此外,c 和 c[0] 使用不同的地址有什么意义?这些问题对某些人来说可能是不必要的,但我很好奇 Python 如何将地址分配给各种数据类型,特别是(此处)字符串。

根据您计算机的体系结构,数据类型将以不同的字节长度存储在内存中。例如,字符串中的每个 ASCII 字符都需要一个字节来存储它,而整数可以以任何位长度存储,最大限制取决于所存储数字的大小。我不太确定,但 python 也可能在分配的内存的不同区域存储不同的数据类型。

Python 在分配的内存中也存储了比你给它的变量更多的东西。该区域的 IDE 也是 运行。所以在两次分配之间可能已经存储了一些其他变量。

对于更新 #1,请查看 this

id 恰好是 CPython 中的地址是一个实现细节;它们只保证对于同时存在的对象是不同的。

您观察到的分组是因为 CPython 预先创建了一些对象,包括 -5256 以及 TrueFalse.在一般情况下,这些值不会出现在任何其他地址,这之所以成为可能,是因为它们属于不可变类型。

第二个问题,关于字符串的切片,是因为 Python 的字符串对象不相互引用。没有字符类型,所以从字符串中提取一个字符会产生一个新的字符串。同样,其中一些可能会被缓存(驻留字符串)。字符串对象的地址不一定是其内容的地址。

您熟悉的 C 类型可以使用 ctypes 访问,但这样做通常很笨拙且有风险。例如,如果您将一个 Python 字符串传递给一个改变 C 字符串的函数,您就破坏了该字符串本身; Python 期望字符串是不可变的,并且可以共享它们并缓存它们的哈希值。

首先,我们应该从 Python 的工作方式与 C 不同。在 C 中,数组只是一块内存。在 Python 中,它是一个对象。 cc[0]id() 与那个结果不一样。

其次,你应该意识到 Python 中的每一个东西都是一个对象。在 C 语言中,当您执行类似 c[0] 的操作时,您是在请求一系列内存位置中的第一个值。在Python中就不一定了。对于标准列表,它由一个数组支持,但它的地址对您是隐藏的。你看到的是通过 id() 的对象的地址。在这种情况下,c 是一个字符串,但 c[0] 也是一个字符串(Python 中没有字符类型)。这意味着当您请求 c[0] 时,Python 正在创建一个新字符串来表示您请求的字符(或者更确切地说,子字符串)。幸运的是,Python 实际上并不是每次都创建一个新字符串,因为 Python 会自动保留 1 个字符的字符串。

此外,请记住,Python 对象有一个结构,它也会消耗内存。关于 C 的最好的事情之一是能够很好地控制内存布局,但你在 Python 中失去了这方面。另一方面是你不必手动分配和释放内存,这是一种解脱(我做了很多 C 和 Python 编程,所以我看到了好处)。

第三,在 Python 中发生了 很多 的内存分配和释放。根据 Python 的构建方式以及分配内存的底层操作系统策略,可能会发生任何数量的事情导致地址不按顺序增加。但是由于 一切 都是一个对象,所以一切都在进行底层分配。

I had this doubt because I learnt C++ first (to be honest, Turbo C++) and the way strings' addresses are defined in Python is very different from what happens in C++. I guess this is okay in Python as we cannot access an object via it's address in Python, am I right?

是,也不是。当你说 c[0] 时,在引擎盖下是一个特殊的方法 运行 从字符串中检索子字符串。这与您在 C++ 中获得的不同。但是,Python 确实有效地将字符串存储为字节序列。因此,仅仅因为您看不到效率检查地址,并不意味着它不存在。另外,正如我上面提到的,c[0] return 是一个新字符串,表示您想要的子字符串。 Python 在这里很聪明,它将 return 一个 1 个字符的字符串,但它将是一个内部字符串。可以看到有些字母有相同的地址:

>>> for c in "hobo":
...     print c, id(c)
...
h 4434994600
o 4434861432
b 4434859712
o 4434861432

你可以看到 "o" 的字符串共享相同的地址--顺便说一句,示例是 Python 2,但相同质量存在于 Python 3.

你是对的,你不能通过地址访问对象——至少这不是语言的特性。如何生成 ID 是一个实现细节,但您应该指望每个 Python 解释器都这样做。

Also, what is the point in having different addresses for c and c[0]? These questions may be unnecessary for some, but I am too curious to know how Python allocates addresses to various datatypes, specially (here) strings.

我在上面解释过,但回顾一下:cc[0] 与 C 中的不同。在 Python 中,第一个是字符串,第二个是请求子字符串包含字符串的第一个字符。

Python 确实在很多地方使用了 arena 风格的内存管理方案,但在大多数情况下你不需要关心它。如果你很好奇,我建议你看看Python source codePython 子目录有许多语言和低级运行时支持位。并且还意识到 Python 也预缓存了一些东西,这也可以解释您在上面看到的地址差异。