首先,我们应该首先说明 Python 的工作方式与 C 不同。在 C 中,数组只是一块内存。在 Python 中,它是一个对象。因此c 和c[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 的构建方式以及分配内存的底层操作系统策略,可能会发生任何数量的事情以导致地址不按顺序增加。但由于所有东西都是一个对象,所以对所有东西都有一个底层分配。
我有这个疑问是因为我首先学习了 C++(老实说,Turbo C++),并且在 Python 中定义字符串地址的方式与在 C++ 中发生的情况非常不同。我想这在 Python 中是可以的,因为我们无法通过 Python 中的地址访问对象,对吗?
是的,也不是。当您说c[0] 时,引擎盖下正在运行一种特殊方法来从字符串中检索子字符串。这与您在 C++ 中得到的不同。然而,Python 确实有效地将字符串存储为字节序列。因此,仅仅因为您看不到效率检查地址,并不意味着它不存在。另外,正如我上面提到的,c[0] 返回一个新字符串,它代表您想要的子字符串。 Python 在这里很聪明,它会返回一个 1 字符的字符串,但它会是一个实习生字符串。可以看到有些字母的地址是一样的:
>>> for c in "hobo":
... print c, id(c)
...
h 4434994600
o 4434861432
b 4434859712
o 4434861432
您可以看到"o" 的字符串共享相同的地址--顺便说一句,示例是 Python 2,但在 Python 3 中存在相同的质量。
你是对的,你不能通过它的地址访问对象——至少这不是语言的特性。如何生成 id 是一个实现细节,但您应该指望每个 Python 解释器都这样做。
另外,为 c 和 c[0] 设置不同的地址有什么意义?这些问题对某些人来说可能是不必要的,但我很想知道 Python 如何将地址分配给各种数据类型,特别是(这里)字符串。
我在上面解释了这一点,但回顾一下:c 和 c[0] 与 C 中的不同。在 Python 中,第一个是字符串,第二个是请求包含字符串第一个字符的子字符串。
Python 确实在许多领域使用了竞技场风格的内存管理方案,但在大多数情况下,您不需要关心这一点。如果你好奇,我建议你看看Python source code。 Python 子目录有许多语言和低级运行时支持位。并且还意识到 Python 也预先缓存了一些东西,这也可以解释您在上面看到的地址差异。