【问题标题】:Why and where python interned strings when executing `a = 'python'` while the source code does not show that?为什么在执行`a ='python'`时python会在哪里插入字符串,而源代码没有显示?
【发布时间】:2014-10-05 16:02:35
【问题描述】:

我正在尝试学习python在字符串对象实现中使用的实习机制。但是在PyObject *PyString_FromString(const char *str)PyObject *PyString_FromStringAndSize(const char *str, Py_ssize_t size)这两个python中,只有当它的大小为0或1时才被困住。

PyObject *
PyString_FromString(const char *str)
{
    fprintf(stdout, "creating %s\n", str);------------[1]
    //...
    //creating...
    /* share short strings */
    if (size == 0) {
        PyObject *t = (PyObject *)op;
        PyString_InternInPlace(&t);
        op = (PyStringObject *)t;
        nullstring = op;
        Py_INCREF(op);
    } else if (size == 1) {
        PyObject *t = (PyObject *)op;
        PyString_InternInPlace(&t);
        op = (PyStringObject *)t;
        characters[*str & UCHAR_MAX] = op;
        Py_INCREF(op);
    }
    return (PyObject *) op;
}

但是对于像a ='python' 这样的较长字符串,如果我修改string_print 来打印地址,它与另一个字符串变量b = 'python 相同。在上面标记为 [1] 的行中,我在 python 创建字符串对象时打印了一条日志,显示在执行a ='python' 时创建了多个字符串,但没有'python'。

>>> a = 'python'
creating stdin
creating stdin
string and size creating (null)
string and size creating a = 'python'
?
creating a
string and size creating (null)
string and size creating (null)
creating __main__
string and size creating (null)
string and size creating (null)
creating <stdin>
string and size creating d
creating __lltrace__
creating stdout
[26691 refs]
creating ps1
creating ps2

那么字符串“python”在哪里创建和实习?

更新 1

请参考@Daniel Darabos 的评论以获得更好的解释。提出这个问题的方式更容易理解。

以下是PyString_InternInPlace添加日志打印命令后的输出。

PyString_InternInPlace(PyObject **p)
{
    register PyStringObject *s = (PyStringObject *)(*p);
    fprintf(stdout, "Interning ");
    PyObject_Print(s, stdout, 0);
    fprintf(stdout, "\n");
    //...
}
>>> x = 'python'
Interning 'cp936'
Interning 'x'
Interning 'cp936'
Interning 'x'
Interning 'python'
[26706 refs]

【问题讨论】:

  • 顺便说一句:你为​​什么要调查这个?这是一个非常不寻常的调查路线。
  • 我认为这是一个有趣的问题,但是您以一种难以理解的方式提出了它。你应该在a = 'python'b == 'python'之后问为什么id(a) == id(b)。就您对 Python 源代码的理解而言,应该只保留长度为 0 和 1 的字符串。那么为什么'python' 显然会被拘留?
  • @zoujyjs 我看不出你的输出中有什么让你认为'python' 被拘留了。可能不是。但是编译代码中的文字可能引用同一个对象。
  • @NedBatchelder 因为这两个变量共享相同的内存地址。我通过修改 string_print 函数来打印它的地址。在 PyString_InternInPlace 中,如果我打印正在实习的对象,我可以在执行 cmd 时看到“Python”和许多其他对象。对不起格式,我用手机回复。
  • @DanielDarabos 看,你完全理解我了~ 为什么? ps:我已经更新了问题和标题。

标签: python python-c-api cpython


【解决方案1】:

字符串文字被编译器转换为字符串对象。这样做的函数是PyString_DecodeEscape,至少在Py2.7中,你还没有说你正在使用什么版本。

更新:

编译器在编译过程中实习了一些字符串,但是当它发生时非常混乱。字符串只需要有标识符-ok 字符:

>>> a = 'python'
>>> b = 'python'
>>> a is b
True
>>> a = 'python!'
>>> b = 'python!'
>>> a is b
False

即使在函数中,字符串字面量也可以被实习:

>>> def f():
...   return 'python'
...
>>> def g():
...   return 'python'
...
>>> f() is g()
True

但如果他们有有趣的角色:

>>> def f():
...   return 'python!'
...
>>> def g():
...   return 'python!'
...
>>> f() is g()
False

如果我返回一对字符串,它们都没有被实习,我不知道为什么:

>>> def f():
...   return 'python', 'python!'
...
>>> def g():
...   return 'python', 'python!'
...
>>> a, b = f()
>>> c, d = g()
>>> a is c
False
>>> a == c
True
>>> b is d
False
>>> b == d
True

故事的寓意:实习是一种依赖于实现的优化,它取决于许多因素。了解它的工作原理会很有趣,但永远不要依赖于它以任何特定方式工作。

【讨论】:

  • 我正在阅读 2.5 的源代码,认为在保留关键方法的同时可能会更容易。我正在开发一个在线游戏项目,该项目使用 python 作为集成在游戏引擎中的脚本系统。我认为如果我对 python 的工作原理以及如何在 c 级别扩展它有所了解,这可能会很有用。谢谢你的回答,我明天去看看。
  • 您需要充分了解 C API 才能将 Python 集成到游戏引擎中。但是你不需要这个问题的详细程度。另外,除非您有充分的理由使用 2.5,否则我建议您使用最新的 2.7 代码。
  • 这并不能回答为什么长度 >1 的字符串似乎会被拘禁的问题。
  • 所以他们没有被拘留?从 REPL 看起来他们是。 id('asd') == id('asd')Truex = id('asd') 然后x == id('asd')False。但如果我先做y = 'asd',那么它就是True。在脚本中,所有文字似乎都被拘留了。编译器中执行此操作的代码在哪里?
  • 进一步研究,编译器在编译期间实习字符串文字。
猜你喜欢
  • 1970-01-01
  • 2011-04-20
  • 2021-05-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
  • 2012-07-26
相关资源
最近更新 更多