我完全坚持这个
>>> s = chr(8263)
>>> x = s[0]
>>> x is s[0]
False
这怎么可能?这是否意味着通过索引访问字符串字符会创建相同字符的新实例?我们来试验一下:
>>> L = [s[0] for _ in range(1000)]
>>> len(set(L))
1
>>> ids = map(id, L)
>>> len(set(ids))
1000
>>>
哎呀浪费字节;)或者它是否意味着str.__getitem__
有隐藏的功能?有人可以解释一下吗?
但这并不是我的意外结束:
>>> s = chr(8263)
>>> t = s
>>> print(t is s, id(t) == id(s))
True True
很明显:t
是s
的别名,因此它们代表相同的对象,并且身份重合。但同样,以下是可能的:
>>> print(t[0] is s[0])
False
s
和t
是同一个对象,那又是什么?
但更糟糕的是:
>>> print(id(t[0]) == id(s[0]))
True
t[0]
和s[0]
未被垃圾收集,被is
运算符视为同一个对象,但具有不同的ID?有人可以解释一下吗?
答案 0 :(得分:7)
这里有两点要做。
首先,Python确实使用__getitem__
调用创建了一个 new 字符,但前提是该字符的序数值大于而不是256。
例如:
>>> s = chr(256)
>>> s[0] is s
True
>>> t = chr(257)
>>> t[0] is t
False
这是因为在内部,编译的getitem
函数检查单个字符的序数值,如果该值为256或更小,则调用get_latin1_char
。这允许共享一些单字符串。否则,将创建一个新的unicode对象。
第二个问题涉及垃圾收集,并显示解释器可以非常快速地重用内存地址。当你写:
>>> s = t # = chr(257)
>>> t[0] is s[0]
False
Python首先创建两个新的单字符串,然后比较它们的内存地址。它们有不同的地址(我们根据上面的解释有不同的对象),因此将对象与is
进行比较会返回False。
另一方面,我们可能会出现看似矛盾的情况:
>>> id(t[0]) == id(s[0])
True
但这是因为解释器在稍后时刻创建新字符串t[0]
时会快速重用s[0]
的内存地址。
如果检查此行产生的字节码(例如,使用dis
- 见下文),您会看到每一侧的地址是一个接一个地分配的(创建一个新的字符串对象然后{{1调用它)。
一旦返回id
,对象t[0]
的引用就会降为零(我们现在正在对整数进行比较,而不是对象本身)。这意味着id(t[0])
可以在以后创建时重用相同的内存地址。
以下是我已注释的行s[0]
的反汇编字节码。
您可以看到id(t[0]) == id(s[0])
的生命周期在创建t[0]
之前结束(没有对它的引用),因此可以重用它的内存。
s[0]
答案 1 :(得分:0)
is
比较身份和==
比较值。请检查此doc
每个对象都有一个标识,一个类型和一个值。对象的身份 一旦创建就永远不会改变;你可能会认为它是 对象在内存中的地址。 'is'运算符比较了。的身份 两个对象; id()函数返回一个表示它的整数 身份(目前作为其地址实施)。对象的类型是 也是不可改变的。