Python 怎么知道有人在遍历字典?
如果有人尝试:
my_dict = {1: 1}
for key in my_dict:
my_dict.pop(key)
一个人会得到:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration
Python 会抛出错误,因为您在循环时更改了 dict 的大小。
Python 如何知道发生了这种情况,并且可以通过编程方式覆盖此功能以便代码运行吗?
在有人问“我为什么要这样做”这个不可避免的问题之前:我不想。我在问一个问题。这叫做好奇心。
例如:
假设我有一个包含 5 个项目的字典。上面的代码应该简单地删除字典中的所有项目!
回答
如果您在 Python 源代码中搜索“字典在迭代期间改变大小”,您会发现Objects/dictobject.c:
static PyObject*
dictiter_iternextkey(dictiterobject *di)
{
/* ... omitted ... */
if (di->di_used != d->ma_used) {
PyErr_SetString(PyExc_RuntimeError,
"dictionary changed size during iteration");
di->di_used = -1; /* Make this state sticky */
return NULL;
}
该ma_used字段只是字典中的项目数,如 中所述dictobject.h:
/* Number of items in the dictionary */
Py_ssize_t ma_used;
并且di_used只是迭代器创建时该值的副本。
您不能以编程方式更改此设置,至少不能以任何合理的方式进行更改(我们不要使用 monkey-patch dict)。如果愿意,您可以创建自己的字典类型,并定义自己的行为不同的迭代器。
Python 这样做的原因是因为当您迭代一个正在更改的哈希表时,很难弄清楚“正确”的事情是什么。
编写你自己的哈希表实现是一个很好的练习,你会很快发现问题......当你在哈希表中插入或删除条目时,它可能会改变其他条目的顺序——迭代器是否可以接受跳过条目,或两次返回相同的条目?可能不是。你能创建一个提供你想要的迭代行为的数据结构吗?是的,但它很复杂,并且执行此操作的哈希表在其他情况下可能会表现更差。