有人能否解释一下这两种创建列表的方式之间的区别? 它们是同样的东西吗? 如果不是,我应该使用哪一个?
squares1 = [x**2 for x in range(1, 11)]
squares2 = list(x**2 for x in range(1, 11))
有人能否解释一下这两种创建列表的方式之间的区别? 它们是同样的东西吗? 如果不是,我应该使用哪一个?
squares1 = [x**2 for x in range(1, 11)]
squares2 = list(x**2 for x in range(1, 11))
从下面可以看出,它们的性能略有不同:
squares1 = [x**2 for x in range(1, 11)]
每次循环平均需要 3.07 微秒,标准差为 70 纳秒 (7 次运行,每次循环执行 100000 次)
squares2 = list(x**2 for x in range(1, 11))
每次循环的平均值为3.65微秒,标准差为35.6纳秒(7次运行,每次循环100000次)
这可能主要是因为在case 1中同时迭代和生成列表值。
在case 2中,您会在迭代过程中生成值,然后在结束时将其转换为列表,并将其存储为给定变量。
我认为,第一种方法是直接通过列表推导式将squares1初始化为列表。
另一种方法首先创建生成器类对象,然后将其转换为列表。我认为第一种方法更有效率。
虽然列表和生成器之间有一些小差异,但就我的知识和经验而言,列表可以更快地完成工作,而生成器则可以惰性地产生单个结果以供每次迭代使用。对于大多数任务,我建议选择列表。
0 LOAD_CONST 0 (<code object <listcomp> at 0x7fc95aea9ed0, file "<dis>", line 1>)
2 LOAD_CONST 1 ('<listcomp>')
4 MAKE_FUNCTION 0
6 LOAD_NAME 0 (range)
8 LOAD_CONST 2 (1)
10 LOAD_CONST 3 (11)
12 CALL_FUNCTION 2
14 GET_ITER
16 CALL_FUNCTION 1
18 RETURN_VALUE
第二个变成
0 LOAD_NAME 0 (list)
2 LOAD_CONST 0 (<code object <genexpr> at 0x7fc95aea9ed0, file "<dis>", line 1>)
4 LOAD_CONST 1 ('<genexpr>')
6 MAKE_FUNCTION 0
8 LOAD_NAME 1 (range)
10 LOAD_CONST 2 (1)
12 LOAD_CONST 3 (11)
14 CALL_FUNCTION 2
16 GET_ITER
18 CALL_FUNCTION 1
20 CALL_FUNCTION 1
22 RETURN_VALUE
这意味着第二种方法需要多两个指令,使其变慢,即使只是微小的变化。
在我的笔记本电脑上,第一种方法的一百万次迭代需要4.651秒,而第二种方法的一百万次迭代需要5.483秒。
next
的呼叫,最终进入您的列表。 - juanpa.arrivillaga(x**2 for x in range(1000))
%%timeit
[i**2 for i in range(1000)]
Out:
170 µs ± 774 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit
list(i**2 for i in range(1000))
Out:
187 µs ± 2.45 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
list(map(math.pow, range(1, 11), repeat(2)))
,但这主要是个人口味问题。 - Reut Sharabanilambda
没有关系,它是一个生成器表达式,会被实例化为一个列表。 - juanpa.arrivillaga