Python中未分配的字符串如何在内存中有地址?

Usa*_*agi 45 python memory-address

谁可以给我解释一下这个?所以我一直在python中使用id()命令,并遇到了这个:

>>> id('cat')
5181152
>>> a = 'cat'
>>> b = 'cat'
>>> id(a)
5181152
>>> id(b)
5181152
Run Code Online (Sandbox Code Playgroud)

除了一部分之外,这对我有意义:字符串'cat'在我将其分配给变量之前在内存中有一个地址.我可能只是不明白内存寻址是如何工作的,但是有人可以向我解释这一点,或者至少告诉我应该读一下内存寻址吗?

所以这一切都很好,但这让我更加困惑:

>>> a = a[0:2]+'t'
>>> a
'cat'
>>> id(a)
39964224
>>> id('cat')
5181152
Run Code Online (Sandbox Code Playgroud)

这让我感到很奇怪,因为'cat'是一个地址为5181152的字符串,但是新的a具有不同的地址.所以,如果内存中有两个'cat'字符串,为什么不为id('cat')打印两个地址?我最后的想法是连接与地址的变化有关,所以我尝试了这个:

>>> id(b[0:2]+'t')
39921024
>>> b = b[0:2]+'t'
>>> b
'cat'
>>> id(b)
40000896
Run Code Online (Sandbox Code Playgroud)

我本来可以预测ID是相同的,但事实并非如此.思考?

kin*_*all 52

Python相当积极地重用字符串文字.它这样做的规则是依赖于实现的,但CPython使用了我所知道的两个:

  • 仅包含在Python标识符中有效的字符的字符串被实现,这意味着它们存储在一个大表中,并在任何出现的地方重用.因此,无论您在何处使用"cat",它始终引用相同的字符串对象.
  • 无论内容和长度如何,都会重复使用同一代码块中的字符串文字.如果你把整个Gettysburg地址的字符串文字放在一个函数中,两次,它们都是相同的字符串对象.在单独的函数中,它们是不同的对象: def foo(): return "pack my box with five dozen liquor jugs" def bar(): return "pack my box with five dozen liquor jugs" assert foo() is bar() # AssertionError

两个优化都在编译时完成(即,生成字节码时).

另一方面,类似于chr(99) + chr(97) + chr(116)字符串表达式,其值为字符串"cat".在像Python这样的动态语言中,它的值在编译时chr()是不可知的(是一个内置函数,但你可能已经重新赋值)所以它通常不会被实现.因此它id()不同于"cat".但是,您可以使用该intern()函数强制执行字符串.从而:

id(intern(chr(99) + chr(97) + chr(116))) == id("cat")   # True
Run Code Online (Sandbox Code Playgroud)

正如其他人所提到的那样,实习是可能的,因为字符串是不可变的.这是不可能改变"cat""dog",换句话说.您必须生成一个新的字符串对象,这意味着指向同一字符串的其他名称不会受到影响.

同样,Python也会"c" + "a" + "t"在编译时将仅包含常量(例如)的表达式转换为常量,如下面的反汇编所示.根据上述规则,这些将被优化为指向相同的字符串对象.

>>> def foo(): "c" + "a" + "t"
...
>>> from dis import dis; dis(foo)
  1           0 LOAD_CONST               5 ('cat')
              3 POP_TOP
              4 LOAD_CONST               0 (None)
              7 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

  • 哇,恭喜,那些金徽章来之不易!此外,我尝试了葛底斯堡地址的字符串文字,Python 将其实习,所以我很确定它非常积极地做到了这一点。 (2认同)

Dav*_*nan 47

'cat'有一个地址,因为你创建它是为了传递给它id().您尚未将其绑定到名称,但该对象仍然存在.

Python缓存并重用短字符串.但是,如果通过串联组装字符串,则会绕过搜索缓存并尝试重用的代码.

请注意,字符串缓存的内部工作原理是纯粹的实现细节,不应该依赖它.


Ned*_*der 17

所有值必须位于内存中的某个位置.这就是id('cat')产生价值的原因.你把它称为"不存在的"字符串,但它显然确实存在,它还没有分配给一个名称.

字符串是不可变的,所以解释可以做聪明的,比如让文字的所有实例'cat'是相同的对象,以便id(a)id(b)是相同的.

对字符串进行操作将产生新字符串.这些字符串可能与具有相同内容的先前字符串相同或不同.

请注意,所有这些细节都是CPython的实现细节,它们可以随时更改.您不需要在实际程序中关注这些问题.


Sin*_*ion 8

Python变量与其他语言中的变量(例如,C)不同.

在许多其他语言中,变量是内存中位置的名称.在这些语言中,不同类型的变量可以引用不同类型的位置,并且相同的位置可以被赋予多个名称.在大多数情况下,给定的内存位置可能会不时更改数据.还有间接引用内存位置的方法(int *p包含地址,在该地址的内存位置,有一个整数.)但是变量引用的实际位置不能改变; 变量位置.这些语言中的变量赋值实际上是"查找此变量的位置,并将此数据复制到该位置"

Python无法正常工作.在python中,实际对象进入某个内存位置,变量就像位置的标记.Python以与管理变量的方式不同的方式管理存储的值.从本质上讲,python中的赋值意味着"查找此变量的信息,忘记它已引用的位置,并将其替换为此新位置".没有数据被复制.

像python一样工作的langauges的一个共同特征(与我们之前讨论的第一种相反)是某些类型的对象以特殊方式管理; 相同的值被缓存,以便它们不占用额外的内存,因此可以非常容易地比较它们(如果它们具有相同的地址,它们是相同的).这个过程称为实习 ; 虽然动态创建的字符串可能不是,但所有python字符串文字都被实现(除了一些其他类型).

在您的确切代码中,语义对话框将是:

# before anything, since 'cat' is a literal constant, add it to the intern cache
>>> id('cat') # grab the constant 'cat' from the intern cache and look up 
              # it's address
5181152
>>> a = 'cat' # grab the constant 'cat' from the intern cache and 
              # make the variable "a" point to it's location 
>>> b = 'cat' # do the same thing with the variable "b"
>>> id(a) # look up the object "a" currently points to, 
          # then look up that object's address
5181152
>>> id(b) # look up the object "b" currently points to, 
          # then look up that object's address
5181152
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

2010 次

最近记录:

8 年,8 月 前