我应该使用Python中的枚举迭代列表时创建副本 [英] Should I create a copy when iterating over a list using enumerate in Python

查看:114
本文介绍了我应该使用Python中的枚举迭代列表时创建副本的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在回答这个问题时,我发现了一些我从未想过的Python(用户指出)。

When answering this question, I came across something I never thought about in Python (pointed by a user).

基本上,我已经知道了(这是一个有趣的主题关于它)我在Python中改变列表时必须进行复制,以避免出现奇怪的行为。

Basically, I already know (here's an interesting thread about it) that I have to make a copy when iterating while mutating a list in Python in order to avoid strange behaviors.

现在,我的问题是,使用枚举克服这个问题?

Now, my question is, is using enumerate overcoming that problem ?

test_list = [1,2,3,4]
for index,item in enumerate(test_list):
    if item == 1:
        test_list.pop(index)

此代码是否安全或我应该使用,

Would this code be considered safe or I should use,

for index,item in enumerate(test_list[:]):


推荐答案

首先,让我们回答你的直接问题:

First, let’s answer your direct question:

枚举对任何人都没有帮助在这里。它就好像它拥有一个底层迭代器的迭代器(至少在CPython中,它正是它所做的),所以任何对列表迭代器不合法或不安全的东西都是不合法或不安全的。使用包含该列表迭代器的枚举对象。

enumerate doesn’t help anything here. It works as if it held an iterator to the underlying iterable (and, at least in CPython, that’s exactly what it does), so anything that wouldn’t be legal or safe to do with a list iterator isn’t legal or safe to do with an enumerate object wrapped around that list iterator.

您的原始用例设置 test_list [index] = new_value - 在实践中是安全的 - 但我不确定保证是否安全。

Your original use case—setting test_list[index] = new_value—is safe in practice—but I’m not sure whether it’s guaranteed to be safe or not.

您的新用例调用 test_list.pop(index) - 可能安全。

Your new use case—calling test_list.pop(index)—is probably not safe.

列表迭代器最明显的实现基本上只是对列表的引用和该列表的索引。因此,如果您在当前位置或该位置的左侧插入或删除,您肯定会破坏迭代器。例如,如果您删除 lst [i] ,则会将所有内容从 i + 1 转移到最后一个位置,所以当你转到 i + 1 时,你正在跳过原来的 i + 1 值,因为它现在是 i 。但是如果你插入或删除当前位置的右边,这不是问题。

The most obvious implementation of a list iterator is basically just a reference to the list and an index into that list. So, if you insert or delete at the current position, or to the left of that position, you’re definitely breaking the iterator. For example, if you delete lst[i], that shifts everything from i + 1 to the end up one position, so when you move on to i + 1, you’re skipping over the original i + 1th value, because it’s now the ith. But if you insert or delete to the right of the current position, that’s not a problem.

因为 test_list.pop(index)删除当前位置或其左侧,即使使用此实现也不安全。 (当然,如果你仔细编写了你的​​算法,以便在命中之后跳过这个值永远不会重要,甚至可能就好了。但是更多的算法不会处理它。)

Since test_list.pop(index) deletes at or left of the current position, it's not safe even with this implementation. (Of course if you've carefully written your algorithm so that skipping over the value after a hit never matters, maybe even that's fine. But more algorithms won't handle that.)

可以想象,Python实现可以将原始指针存储到用于列表存储的数组中的当前位置。这意味着插入任何地方可能会破坏迭代器,因为插入可能导致整个列表重新分配到新内存。如果实施有时会在缩小时重新分配列表,那么可以删除任何地方。我不认为Python不允许执行所有这些,所以如果你想成为偏执,那么在迭代时永远不会插入或删除可能更安全。

It’s conceivable that a Python implementation could instead store a raw pointer to the current position in the array used for the list storage. Which would mean that inserting anywhere could break the iterator, because an insert can cause the whole list to get reallocated to new memory. And so could deleting anywhere, if the implementation sometimes reallocates lists on shrinking. I don't think the Python disallows implementations that do all of this, so if you want to be paranoid, it may be safer to just never insert or delete while iterating.

如果您只是替换现有值,很难想象在任何合理的实现下如何破坏迭代器。但是,据我所知,语言参考和列表库引用 1 实际上并未对列表的实现做出任何承诺迭代器。 2

If you’re just replacing an existing value, it’s hard to imagine how that could break the iterator under any reasonable implementation. But, as far as I'm aware, the language reference and list library reference1 don't actually make any promises about the implementation of list iterators.2

因此,你是否关心我的实施中的安全,每个实现的每个实现安全都取决于你日期,在每个可以想象的(对我而言)实施中是安全的,或者通过参考保证安全。

So, it's up to you whether you care about "safe in my implementation", "safe in every implementation every written to date", "safe in every conceivable (to me) implementation", or "guaranteed safe by the reference".

我认为大多数人在迭代期间愉快地替换列表项,但要避免收缩或增加清单。但是,肯定有生产代码,至少删除了迭代器的右边。

I think most people happily replace list items during iteration, but avoid shrinking or growing the list. However, there's definitely production code out there that at least deletes to the right of the iterator.

1。我相信教程只是说某些地方在迭代它时永远不会修改任何数据结构 - 但这就是教程。始终遵循该规则肯定是安全的,但遵循不太严格的规则也是安全的。

1. I believe the tutorial just says somewhere to never modify any data structure while iterating over it—but that’s the tutorial. It’s certainly safe to always follow that rule, but it may also be safe to follow a less strict rule.

2。除非是 key 函数或其他任何东西试图以任何方式访问列表 sort ,结果未定义。

2. Except that if the key function or anything else tries to access the list in any way in the middle of a sort, the result is undefined.

这篇关于我应该使用Python中的枚举迭代列表时创建副本的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆