重用枚举创建的 Python 个对象
Reuse of Python object created by enumerate
我有一个列表列表,我正在搜索这些列表以查找重复索引。
listA = [[1,2,3],[3,4,5],[6,7,8],[1,2,3]]
listA_set = [[1,2,3],[3,4,5],[6,7,8]]
enum_listA = enumerate(listA)
listA_indices = []
for i in listA_set:
listA_indices([j[0] for j in enum_listA if j[1] == i])
预期结果是:
listA_indices = [[0,3],[1],[2]]
但我得到的是:
listA_indices = [[0,3],[],[]]
如果我在内联 for 循环中包含枚举(参见下面的示例),我会收到正确的答案,但速度会显着降低。如何在不丢失存储在 enum_listA?
中的枚举信息的情况下执行此任务
for i in listA_set:
listA_indices([j[0] for j in enumerate(listA) if j[1] == i])
您看到速度显着降低(按照您的术语)的原因是您的第二个正确版本实际上正在计算您正在寻找的值。
因为 enumerate 产生了一个生成器,一旦你消耗了它的值,你就不能再产生它们了。您提供的第二个版本是使用枚举的正确方法。
不是,我完全不确定您的代码是如何工作的。你调用的是一个列表,一个不可调用的对象
listA_indices([...])
也许,你是故意的
listA_indices += [...]
enumerate
returns 一个只能迭代一次的迭代器。所以一旦它用完了,也就是你在第一次 for 循环迭代的列表理解中迭代它之后的情况,它就不会产生更多的项目。
如果您想保留信息,则必须将该数据实际存储在内存中,例如作为列表:
enum_listA = list(enumerate(listA))
请注意,这有效地复制了列表中的信息并添加了索引,因此这会浪费大量额外的内存,并且最终可能不会比一遍又一遍地重新创建枚举对象更有效。
然而,您看到的性能差异来自于这样一个事实,即在第一次循环迭代之后,枚举器为空,因此列表理解不再运行以进行后续迭代。
使用 dict
和 set
,您的代码将 运行 更有效:
from collections import defaultdict
st = set(map(tuple, listA_set))
d = defaultdict(list)
for i, ele in enumerate(map(tuple,listA)):
if ele in st:
d[ele].append(i)
print(list(d.items()))
print(list(d.values()))
输出:
[((3, 4, 5), [1]), ((6, 7, 8), [2]), ((1, 2, 3), [0, 3])]
[[1], [2], [0, 3]]
如果要保持先见顺序:
from collections import OrderedDict
d = OrderedDict()
for i, ele in enumerate(map(tuple, listA)):
if ele in st:
d.setdefault(ele, []).append(i)
print(list(d.items()))
print(list(d.values()))
输出:
[((1, 2, 3), [0, 3]), ((3, 4, 5), [1]), ((6, 7, 8), [2])]
[[0, 3], [1], [2]]
无论您使用枚举的何种方式,您的复杂度仍然是二次的,这将比您自己的方法快得多。
随机数据集上的一些计时:
In [27]: from random import randint
In [28]: listA_set = [[randint(1,20) for _ in range(10)] for _ in range(2000)]
In [29]: listA = [[randint(1,20) for _ in range(10)] for _ in range(3000)]
In [30]: %%timeit
listA_indices = []
for i in listA_set:
listA_indices.append([j[0] for j in enumerate(listA) if j[1] == i])
....:
1 loops, best of 3: 696 ms per loop
In [31]: %%timeit
st = set(map(tuple, listA_set))
from collections import OrderedDict
d = OrderedDict()
for i, ele in enumerate(map(tuple,listA)):
if ele in st:
d.setdefault(ele, []).append(i)
....:
1000 loops, best of 3: 1.49 ms per loop
~400
倍。
我有一个列表列表,我正在搜索这些列表以查找重复索引。
listA = [[1,2,3],[3,4,5],[6,7,8],[1,2,3]]
listA_set = [[1,2,3],[3,4,5],[6,7,8]]
enum_listA = enumerate(listA)
listA_indices = []
for i in listA_set:
listA_indices([j[0] for j in enum_listA if j[1] == i])
预期结果是:
listA_indices = [[0,3],[1],[2]]
但我得到的是:
listA_indices = [[0,3],[],[]]
如果我在内联 for 循环中包含枚举(参见下面的示例),我会收到正确的答案,但速度会显着降低。如何在不丢失存储在 enum_listA?
中的枚举信息的情况下执行此任务for i in listA_set:
listA_indices([j[0] for j in enumerate(listA) if j[1] == i])
您看到速度显着降低(按照您的术语)的原因是您的第二个正确版本实际上正在计算您正在寻找的值。
因为 enumerate 产生了一个生成器,一旦你消耗了它的值,你就不能再产生它们了。您提供的第二个版本是使用枚举的正确方法。
不是,我完全不确定您的代码是如何工作的。你调用的是一个列表,一个不可调用的对象
listA_indices([...])
也许,你是故意的
listA_indices += [...]
enumerate
returns 一个只能迭代一次的迭代器。所以一旦它用完了,也就是你在第一次 for 循环迭代的列表理解中迭代它之后的情况,它就不会产生更多的项目。
如果您想保留信息,则必须将该数据实际存储在内存中,例如作为列表:
enum_listA = list(enumerate(listA))
请注意,这有效地复制了列表中的信息并添加了索引,因此这会浪费大量额外的内存,并且最终可能不会比一遍又一遍地重新创建枚举对象更有效。
然而,您看到的性能差异来自于这样一个事实,即在第一次循环迭代之后,枚举器为空,因此列表理解不再运行以进行后续迭代。
使用 dict
和 set
,您的代码将 运行 更有效:
from collections import defaultdict
st = set(map(tuple, listA_set))
d = defaultdict(list)
for i, ele in enumerate(map(tuple,listA)):
if ele in st:
d[ele].append(i)
print(list(d.items()))
print(list(d.values()))
输出:
[((3, 4, 5), [1]), ((6, 7, 8), [2]), ((1, 2, 3), [0, 3])]
[[1], [2], [0, 3]]
如果要保持先见顺序:
from collections import OrderedDict
d = OrderedDict()
for i, ele in enumerate(map(tuple, listA)):
if ele in st:
d.setdefault(ele, []).append(i)
print(list(d.items()))
print(list(d.values()))
输出:
[((1, 2, 3), [0, 3]), ((3, 4, 5), [1]), ((6, 7, 8), [2])]
[[0, 3], [1], [2]]
无论您使用枚举的何种方式,您的复杂度仍然是二次的,这将比您自己的方法快得多。
随机数据集上的一些计时:
In [27]: from random import randint
In [28]: listA_set = [[randint(1,20) for _ in range(10)] for _ in range(2000)]
In [29]: listA = [[randint(1,20) for _ in range(10)] for _ in range(3000)]
In [30]: %%timeit
listA_indices = []
for i in listA_set:
listA_indices.append([j[0] for j in enumerate(listA) if j[1] == i])
....:
1 loops, best of 3: 696 ms per loop
In [31]: %%timeit
st = set(map(tuple, listA_set))
from collections import OrderedDict
d = OrderedDict()
for i, ele in enumerate(map(tuple,listA)):
if ele in st:
d.setdefault(ele, []).append(i)
....:
1000 loops, best of 3: 1.49 ms per loop
~400
倍。