Chr*_*549 12 python python-3.x
不确定标题是否是正确的术语。
如果您必须比较 2 个字符串 (A,B) 中的字符并计算 B 中字符与 A 的匹配次数:
sum([ch in A for ch in B])
Run Code Online (Sandbox Code Playgroud)
在 %timeit 上比
sum(ch in A for ch in B)
Run Code Online (Sandbox Code Playgroud)
我知道第一个将创建一个 bool 列表,然后对 1 的值求和。第二个是生成器。我不清楚它在内部做什么以及为什么它变慢了?
谢谢。
使用 %timeit 结果进行编辑:
10 个字符
生成器表达式
列表
10000 个循环,最好的 3 个:每个循环 112 µs
10000 个循环,最好的 3 个:每个循环 94.6 µs
1000 个字符
生成器表达式
列表
100 个循环,最好的 3 个:每个循环 8.5 毫秒
100 个循环,最好的 3 个:每个循环 6.9 毫秒
10,000 个字符
生成器表达式
列表
10 个循环,最好的 3 个:每个循环 87.5 毫秒
10 个循环,最好的 3 个:每个循环 76.1 毫秒
100,000 个字符
生成器表达式
列表
1 个循环,3 个最佳:每个循环 908 毫秒
1 个循环,最好的 3 个:每个循环 840 毫秒
Mar*_*hac 10
我查看了每个构造的反汇编(使用dis)。我通过声明这两个函数来做到这一点:
def list_comprehension():
return sum([ch in A for ch in B])
def generation_expression():
return sum(ch in A for ch in B)
Run Code Online (Sandbox Code Playgroud)
然后调用dis.dis每个函数。
对于列表理解:
0 BUILD_LIST 0
2 LOAD_FAST 0 (.0)
4 FOR_ITER 12 (to 18)
6 STORE_FAST 1 (ch)
8 LOAD_FAST 1 (ch)
10 LOAD_GLOBAL 0 (A)
12 COMPARE_OP 6 (in)
14 LIST_APPEND 2
16 JUMP_ABSOLUTE 4
18 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
对于生成器表达式:
0 LOAD_FAST 0 (.0)
2 FOR_ITER 14 (to 18)
4 STORE_FAST 1 (ch)
6 LOAD_FAST 1 (ch)
8 LOAD_GLOBAL 0 (A)
10 COMPARE_OP 6 (in)
12 YIELD_VALUE
14 POP_TOP
16 JUMP_ABSOLUTE 2
18 LOAD_CONST 0 (None)
20 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
实际求和的反汇编是:
0 LOAD_GLOBAL 0 (sum)
2 LOAD_CONST 1 (<code object <genexpr> at 0x7f49dc395240, file "/home/mishac/dev/python/kintsugi/KintsugiModels/automated_tests/a.py", line 12>)
4 LOAD_CONST 2 ('generation_expression.<locals>.<genexpr>')
6 MAKE_FUNCTION 0
8 LOAD_GLOBAL 1 (B)
10 GET_ITER
12 CALL_FUNCTION 1
14 CALL_FUNCTION 1
16 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)
但是这个sum反汇编在你的两个例子之间是不变的,唯一的区别是generation_expression.<locals>.<genexpr>vs的加载list_comprehension.<locals>.<listcomp>(所以只加载一个不同的局部变量)。
前两个反汇编之间的不同字节码指令是LIST_APPEND用于列表理解对的结合YIELD_VALUE和POP_TOP用于发电机的表达。
我不会假装我知道 Python 字节码的内在原理,但我从中收集到的是,生成器表达式是作为队列实现的,在队列中生成值然后弹出。这种弹出不一定发生在列表理解中,这让我相信使用生成器会产生少量开销。
现在这并不意味着生成器总是会变慢。生成器在内存效率方面表现出色,因此会有一个阈值 N,使得列表推导在这个阈值之前会表现得稍微好一些(因为内存使用不会成为问题),但是在这个阈值之后,生成器的表现会明显更好。
| 归档时间: |
|
| 查看次数: |
823 次 |
| 最近记录: |