4

我一直在研究一些将 python(osx 上的 2.7)文件作为配置文件加载的极端案例。如果我循环运行 execfile,我想看看行为是什么。我预计会发生内存不足错误或大量交换,但当我得到不同的结果时,我感到相当惊讶。

我设置了一个测试场景如下:

'd' python 脚本:

#!/usr/bin/python
x = 0
execfile("d1")

'd1' python 脚本:

#!/usr/bin/python
x += 1
print "x = %d" % x
execfile("d2")

'd2' python 脚本:

#!/usr/bin/python
x += 1
print "x = %d" % x
execfile("d1")

结果:

$ ./d
x = 1
x = 2
x = 3
... removed for brevity ...
x = 997
x = 998
x = 999
Traceback (most recent call last):
  File "./d", line 5, in <module>
    execfile("d1")
  File "d1", line 5, in <module>
    execfile("d2")
  File "d2", line 5, in <module>
    execfile("d1")
... removed for brevity ...
  File "d1", line 5, in <module>
    execfile("d2")
  File "d2", line 5, in <module>
    execfile("d1")
  File "d1", line 5, in <module>
    execfile("d2")
KeyError: 'unknown symbol table entry'

我只是好奇是否有人可以解释这里发生了什么?为什么执行 execfile ~1000 次后它会停止?

4

1 回答 1

7

从 Python 源代码,Objects/dictobject.c

/* 请注意,由于历史原因,PyDict_GetItem() 会抑制所有错误
 * 可能发生的情况(最初 dicts 只支持字符串键和异常
 * 不可能)。因此,虽然最初的意图是返回 NULL
 * 表示密钥不存在,实际上它可能表示该密钥或错误
 *(被抑制)在计算密钥的哈希时发生,或者一些错误
 *(被抑制)在比较字典内部探针中的键时发生
 * 顺序。后者的一个令人讨厌的例子是当一个 Python 编码的比较
 * 函数遇到堆栈深度错误,这可能导致它返回 NULL
 * 即使密钥存在。
 */

因此,PyDict_GetItem()并不总是正确报告错误。有趣......所以在下面的代码中Python/symtable.c

v = PyDict_GetItem(st->st_blocks, k);
if (v) {
    assert(PySTEntry_Check(v));
    Py_INCREF(v);
}
else {
    PyErr_SetString(PyExc_KeyError,
                    "unknown symbol table entry");
}

查找符号时发生的任何错误(包括内存不足错误)都将转换为KeyError. 这可能是一个错误。

于 2013-10-27T01:15:55.513 回答