sch*_*tte 3 python iteration list
在回答这个问题时,我遇到了一些我在Python中从未想过的事情(由用户指出)。
基本上,我已经知道(这里有一个关于它的有趣线程),在 Python 中迭代和改变列表时我必须制作一个副本,以避免奇怪的行为。
现在,我的问题是,使用enumerate可以克服这个问题吗?
test_list = [1,2,3,4]
for index,item in enumerate(test_list):
if item == 1:
test_list.pop(index)
Run Code Online (Sandbox Code Playgroud)
这段代码被认为是安全的还是我应该使用,
for index,item in enumerate(test_list[:]):
Run Code Online (Sandbox Code Playgroud)
首先,让\xe2\x80\x99s回答你的直接问题:
\n\nenumerate\xe2\x80\x99 在这里没有任何帮助。它的工作原理就好像它持有一个底层可迭代的迭代器(并且,至少在 CPython 中,\xe2\x80\x99 正是它所做的),因此任何 \xe2\x80\x99t 的操作都是合法或安全的列表迭代器与围绕该列表迭代器的枚举对象一起使用是不合法或不安全的。
您原来的用例\xe2\x80\x94设置test_list[index] = new_value\xe2\x80\x94在实践中是安全的\xe2\x80\x94但我\xe2\x80\x99m不确定它\xe2\x80\x99s是否保证安全。
您的新用例 \xe2\x80\x94 调用test_list.pop(index)\xe2\x80\x94 可能不安全。
列表迭代器最明显的实现基本上只是对列表的引用和该列表的索引。因此,如果您在当前位置或该位置的左侧插入或删除,则 \xe2\x80\x99 肯定会破坏迭代器。例如,如果您删除lst[i],则会将所有内容从i + 1到 最后移动一个位置,因此当您移动到 时i + 1,您\xe2\x80\x99 将跳过原始i + 1th 值,因为它\xe2\x80\x99 现在是第ith 值。 。但如果你在当前位置的右边插入或删除,那\xe2\x80\x99s就不是问题了。
由于test_list.pop(index)在当前位置或当前位置左侧进行删除,因此即使使用此实现也是不安全的。(当然,如果您仔细编写了算法,以便在命中后跳过该值无关紧要,也许这样也没关系。但更多的算法无法处理这个问题。)
可以想象,Python 实现可以存储指向用于列表存储的数组中当前位置的原始指针。这意味着插入任何地方插入都可能会破坏迭代器,因为插入可能会导致整个列表重新分配到新内存。如果实现有时会在收缩时重新分配列表,则可以删除任何地方。我不认为 Python 不允许执行所有这些操作,因此,如果您想偏执一些,那么在迭代时从不插入或删除可能会更安全。
\n\n如果您只是替换现有值,则很难想象在任何合理的实现下这会如何破坏迭代器。但是,据我所知,语言参考和list库参考1实际上并没有对列表迭代器的实现做出任何承诺。2
因此,这取决于你是否关心“我的实现中的安全”、“迄今为止编写的每个实现中的安全”、“(对我来说)每个可想象的实现中的安全”或“通过参考保证安全” ”。
\n\n我认为大多数人很乐意在迭代期间替换列表项,但避免缩小或增长列表。然而,肯定有生产代码至少删除了迭代器的右侧。
\n\n1.我相信教程只是说在迭代\xe2\x80\x94时永远不要修改任何数据结构,但是\xe2\x80\x99是教程。始终遵循该规则当然是安全的,但遵循不太严格的规则也可能是安全的。
\n\n2. 除非key函数或其他任何东西尝试在 a 中间以任何方式访问列表sort,结果是未定义的。
| 归档时间: |
|
| 查看次数: |
2593 次 |
| 最近记录: |