关于id类型对象str(在python 2.7中)的某些事情使我感到困惑。该str类型是不可变的,因此我希望一旦创建它,它将始终具有相同的id。我相信我说的话不太好,所以我将发布一个输入和输出序列的示例。
>>> id('so')
140614155123888
>>> id('so')
140614155123848
>>> id('so')
140614155123808
因此与此同时,它一直在变化。但是,在有一个指向该字符串的变量之后,情况发生了变化:
>>> so = 'so'
>>> id('so')
140614155123728
>>> so = 'so'
>>> id(so)
140614155123728
>>> not_so = 'so'
>>> id(not_so)
140614155123728
因此,一旦变量保存了该值,它似乎就冻结了id
。确实,在del so
和之后del not_s
o,id('so')start
的输出再次更改。
这是不相同的行为与(小)整数。
我知道不变与不变之间没有真正的联系id; 我仍然试图找出这种行为的根源。我相信熟悉python内部原理的人会比我少惊讶,因此我正在尝试达到相同的目的…
更新资料
用不同的字符串尝试相同将产生不同的结果…
>>> id('hello')
139978087896384
>>> id('hello')
139978087896384
>>> id('hello')
139978087896384
现在,它是平等的......
CPython并不承诺默认情况下会内生所有字符串,但是实际上,Python代码库中的许多地方确实会重用已经创建的字符串对象。许多Python内部人员使用sys.intern()
函数调用(与C等效)来显式地内生Python字符串,但是除非遇到这些特殊情况之一,否则两个相同的Python字符串文字将产生不同的字符串。
Python还可以自由地重用内存位置,并且Python还将通过在编译时将字节码与代码对象中的字节码一起存储一次来优化不可变文字。Python REPL(交互式解释器)还在名称中存储了最新的表达式结果_,这使事情更加混乱。
因此,你会不时看到相同的ID。
仅运行id(<string literal>)REPL
中的代码行需要经历几个步骤:
该行已编译,其中包括为字符串对象创建一个常量:
>>> compile("id('foo')", '<stdin>', 'single').co_consts
('foo', None)
这将显示已存储的常量以及已编译的字节码;在这种情况下,字符串’foo’和None单例。由产生不可变值的简单表达式可以在此阶段进行优化,请参见下面有关优化器的说明。
在执行时,从代码常量加载字符串,并id()
返回内存位置。结果int值绑定到_,并打印:
>>> import dis
>>> dis.dis(compile("id('foo')", '<stdin>', 'single'))
1 0 LOAD_NAME 0 (id)
3 LOAD_CONST 0 ('foo')
6 CALL_FUNCTION 1
9 PRINT_EXPR
10 LOAD_CONST 1 (None)
13 RETURN_VALUE
该代码对象未被任何内容引用,引用计数降至0,并且该代码对象被删除。结果,字符串对象也是如此。
如果重新运行相同的代码,Python 也许可以将相同的内存位置重新用于新的字符串对象。如果重复此代码,通常会导致打印相同的内存地址。这确实取决于你对Python内存的其他处理方式。
ID重用是不可预测的;如果在此期间垃圾回收器运行以清除循环引用,则可以释放其他内存,并且你将获得新的内存地址。
接下来,Python编译器还将实习存储为常量的任何Python字符串,只要它看起来足够像一个有效的标识符即可。Python 代码对象工厂函数PyCode_New将通过调用来实习任何仅包含ASCII字母,数字或下划线的字符串对象intern_string_constants()
。此函数遍历常量结构,并且对于v在其中找到的任何字符串对象都将执行:
if (all_name_chars(v)) {
PyObject *w = v;
PyUnicode_InternInPlace(&v);
if (w != v) {
PyTuple_SET_ITEM(tuple, i, v);
modified = 1;
}
}
在哪里all_name_chars()
记录为
/* all_name_chars(s): true iff s matches [a-zA-Z0-9_]* */
由于你创建的字符串符合该条件,因此它们会被扣留,这就是为什么你’so’在第二个测试中看到该字符串使用相同的ID的原因:只要保留对扣留版本的引用,则扣留将导致将来的’so’文字重用插入的字符串对象,甚至在新的代码块中并绑定到不同的标识符。在你的第一个测试中,你不会保存对字符串的引用,因此,在重新使用已插入的字符串之前,它们会被丢弃。
顺便说一句,你的新名称so = 'so'
将字符串绑定到包含相同字符的名称。换句话说,你正在创建一个名称和值相等的全局变量。随着Python既对标识符又对限定常量进行实习,你最终将对标识符及其值使用相同的字符串对象:
>>> compile("so = 'so'", '<stdin>', 'single').co_names[0] is compile("so = 'so'", '<stdin>', 'single').co_consts[0]
True
如果创建的字符串不是代码对象常量,或者包含字母+数字+下划线范围之外的字符,则将看到该id()值未被重用:
>>> some_var = 'Look ma, spaces and punctuation!'
>>> some_other_var = 'Look ma, spaces and punctuation!'
>>> id(some_var)
4493058384
>>> id(some_other_var)
4493058456
>>> foo = 'Concatenating_' + 'also_helps_if_long_enough'
>>> bar = 'Concatenating_' + 'also_helps_if_long_enough'
>>> foo is bar
False
>>> foo == bar
True
Python编译器使用窥孔优化器(Python版本<3.7)或功能更强大的AST优化器(3.7及更高版本)来预先计算(折叠)涉及常量的简单表达式的结果。peepholder将其输出限制为长度为20或更短的序列(以防止膨胀的代码对象和内存使用),而AST优化程序对4096个字符的字符串使用单独的限制。这意味着,串联短串只包括名称中的字符可仍然导致实习字符串,如果你当前的Python版本优化限制范围内生成的字符串配合。
例如,在Python 3.7上,'foo' * 20
将产生一个单一的字符串,因为常量折叠会将其转换为单个值,而在Python 3.6或更旧的版本上,仅'foo' *
6会被折叠:
>>> import dis, sys
>>> sys.version_info
sys.version_info(major=3, minor=7, micro=4, releaselevel='final', serial=0)
>>> dis.dis("'foo' * 20")
1 0 LOAD_CONST 0 ('foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo')
2 RETURN_VALUE
和
>>> dis.dis("'foo' * 6")
1 0 LOAD_CONST 2 ('foofoofoofoofoofoo')
2 RETURN_VALUE
>>> dis.dis("'foo' * 7")
1 0 LOAD_CONST 0 ('foo')
2 LOAD_CONST 1 (7)
4 BINARY_MULTIPLY
6 RETURN_VALUE
本文向大家介绍如何在Python中更改不可变字符串的ID?,包括了如何在Python中更改不可变字符串的ID?的使用技巧和注意事项,需要的朋友参考一下 Python中的字符串是不可变的,这意味着一旦创建了字符串,就无法更改它。创建字符串时,如果创建相同的字符串并将其分配给另一个变量,则它们都将指向相同的字符串/内存。例如, 字符串对象的这种重用在Python中称为Interning。相同的字符串具
问题内容: 我读过在Java中不能更改String类型的对象。但是int和char变量可以。为什么?你可以给我一个例子吗? 谢谢。(我是较新的-_-) 问题答案: 正如bzabhi所说,字符串在Java中是不可变的。这意味着字符串对象将永远不会改变。这并不意味着您不能更改字符串变量,只是不能更改字符串的基础内存表示形式。例如: 执行完这些行后,str将指向内存中的新字符串。原始的“ Hello”字
我认为每次更改字符串后,Python字符串的id都必须更改。但我发现真正的行为是不同的。例如,并非输出下面的所有代码字符串都不同: 这就是为什么我认为Python内核正在尝试优化代码,并开始对内存中的字符串进行奇怪的操作。该假设的另一个论点是,常量ID与大小为2的幂的段相关联: 但这其中还有一件奇怪的事。让我们看看随着字符串大小的增加,段大小会发生什么变化: 最后,我们可以尝试近似地将char添加
本文向大家介绍Java中的不可变字符串,包括了Java中的不可变字符串的使用技巧和注意事项,需要的朋友参考一下 在Java中,不可变对象是指那些不能更改或修改(一旦修改)数据的对象。字符串类是不可变的,即一旦我们创建了一个字符串对象,就无法修改其数据。
#include <stdio.h> int main(void) { char p1[] = "Sam"; char *p2 = "Bob"; printf("p1 is %s, p2 is %s\n", p1, p2); return 0; } 技巧 使用gdb调试程序时,可以用“set”命令改变字符串的值,以上面程序为例: (gdb) start Tem
据我所知, 可变字符串可以更改,不可变字符串不能更改。 这里我想这样改变字符串的值, 另一种方法是, 在这两种情况下,我都试图改变str的值。谁能告诉我,这两种情况的区别是什么,让我清楚地了解可变和不可变对象。