是时候在 Python 中分配一个变量了
Time to assign a variable in Python
假设我在一个非常紧凑的循环中有以下内容:
a = func(x)
b = func2(a)
变量a
未在其他任何地方使用。
Python 是自动编译掉对 a
的赋值,还是每次都花时间进行变量赋值?换句话说,这段代码是相同的,还是由于没有分配给 a
?
b = func2(func(x))
Python2.7 与 Python3 的行为是否相同?
可以使用 timeit
轻松检查此类查询。这是 Python2.7
.
的结果
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.29 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.284 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.285 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.283 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.294 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.286 usec per loop
这显示了与描述使用令人敬畏的 dis
模块的其他答案一致的结果。
因此,使用非常有趣的 dis
模块,我们可以查看从您提供的 python 代码生成的实际字节码。为了简单起见,我将 func
和 func2
替换为内置函数(int
和 float
)。
所以我们的来源是这样的:
def assign():
a = int()
b = float(a)
与简化版本相比:
def simple():
b = float(int())
然后从cpython 2.7解释器开始,我们可以看到assign
函数生成的字节码:
dis.dis(assign)
2 0 LOAD_GLOBAL 0 (int)
3 CALL_FUNCTION 0
6 STORE_FAST 0 (a)
3 9 LOAD_GLOBAL 1 (float)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 STORE_FAST 1 (b)
21 LOAD_CONST 0 (None)
24 RETURN_VALUE
如您所见,没有用于删除不必要的中间变量的窥孔优化,与简化的字节码相比,这导致额外的 2 条指令(STORE_FAST a
、LOAD_FAST a
)简单方法:
dis.dis(simple)
2 0 LOAD_GLOBAL 0 (float)
3 LOAD_GLOBAL 1 (int)
6 CALL_FUNCTION 0
9 CALL_FUNCTION 1
12 STORE_FAST 0 (b)
15 LOAD_CONST 0 (None)
18 RETURN_VALUE
这对于 Python 3.5 的 CPython 解释器和 Python 2.7 的 pypy 解释器是一样的。
使用dis模块比较字节码:看起来第二种方法产生的操作
import dis
print(dis.dis('a=f(2);b=g(a)'))
print(dis.dis('b=g(f(2))'))
>>>
1 0 LOAD_NAME 0 (f)
2 LOAD_CONST 0 (2)
4 CALL_FUNCTION 1
6 STORE_NAME 1 (a)
8 LOAD_NAME 2 (g)
10 LOAD_NAME 1 (a)
12 CALL_FUNCTION 1
14 STORE_NAME 3 (b)
16 LOAD_CONST 1 (None)
18 RETURN_VALUE
None
1 0 LOAD_NAME 0 (g)
2 LOAD_NAME 1 (f)
4 LOAD_CONST 0 (2)
6 CALL_FUNCTION 1
8 CALL_FUNCTION 1
10 STORE_NAME 2 (b)
12 LOAD_CONST 1 (None)
14 RETURN_VALUE
None
实际时间将取决于 func()
和 func2()
函数执行的操作。不是最好的例子,但下面给出了一个快速(和肮脏的)测试代码:
import time
def func(x):
return 5
def func2(a):
return 10
t0 = time.time()
x = 10
for i in range(1,10000):
a = func(x)
b = func2(a)
t1 = time.time()
print("Time 1: ", t1-t0)
t2 = time.time()
x = 10
for i in range(1,10000):
b = func2(func(x))
t3 = time.time()
print("Time 2: ", t3-t2)
以上代码的输出为:
Time 1: 0.0029211044311523438
Time 2: 0.002785921096801758
所以是的,我们避免分配 a
的实现在 Pyhton 3 中稍微快一些。
假设我在一个非常紧凑的循环中有以下内容:
a = func(x)
b = func2(a)
变量a
未在其他任何地方使用。
Python 是自动编译掉对 a
的赋值,还是每次都花时间进行变量赋值?换句话说,这段代码是相同的,还是由于没有分配给 a
?
b = func2(func(x))
Python2.7 与 Python3 的行为是否相同?
可以使用 timeit
轻松检查此类查询。这是 Python2.7
.
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.29 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.284 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.285 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.283 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; a=f1(2); b=f2(a)"
1000000 loops, best of 3: 0.294 usec per loop
root:/var# python -m timeit "f1 = lambda x:x; f2 = lambda x: x*2; b=f2(f1(2))"
1000000 loops, best of 3: 0.286 usec per loop
这显示了与描述使用令人敬畏的 dis
模块的其他答案一致的结果。
因此,使用非常有趣的 dis
模块,我们可以查看从您提供的 python 代码生成的实际字节码。为了简单起见,我将 func
和 func2
替换为内置函数(int
和 float
)。
所以我们的来源是这样的:
def assign():
a = int()
b = float(a)
与简化版本相比:
def simple():
b = float(int())
然后从cpython 2.7解释器开始,我们可以看到assign
函数生成的字节码:
dis.dis(assign)
2 0 LOAD_GLOBAL 0 (int)
3 CALL_FUNCTION 0
6 STORE_FAST 0 (a)
3 9 LOAD_GLOBAL 1 (float)
12 LOAD_FAST 0 (a)
15 CALL_FUNCTION 1
18 STORE_FAST 1 (b)
21 LOAD_CONST 0 (None)
24 RETURN_VALUE
如您所见,没有用于删除不必要的中间变量的窥孔优化,与简化的字节码相比,这导致额外的 2 条指令(STORE_FAST a
、LOAD_FAST a
)简单方法:
dis.dis(simple)
2 0 LOAD_GLOBAL 0 (float)
3 LOAD_GLOBAL 1 (int)
6 CALL_FUNCTION 0
9 CALL_FUNCTION 1
12 STORE_FAST 0 (b)
15 LOAD_CONST 0 (None)
18 RETURN_VALUE
这对于 Python 3.5 的 CPython 解释器和 Python 2.7 的 pypy 解释器是一样的。
使用dis模块比较字节码:看起来第二种方法产生的操作
import dis
print(dis.dis('a=f(2);b=g(a)'))
print(dis.dis('b=g(f(2))'))
>>>
1 0 LOAD_NAME 0 (f)
2 LOAD_CONST 0 (2)
4 CALL_FUNCTION 1
6 STORE_NAME 1 (a)
8 LOAD_NAME 2 (g)
10 LOAD_NAME 1 (a)
12 CALL_FUNCTION 1
14 STORE_NAME 3 (b)
16 LOAD_CONST 1 (None)
18 RETURN_VALUE
None
1 0 LOAD_NAME 0 (g)
2 LOAD_NAME 1 (f)
4 LOAD_CONST 0 (2)
6 CALL_FUNCTION 1
8 CALL_FUNCTION 1
10 STORE_NAME 2 (b)
12 LOAD_CONST 1 (None)
14 RETURN_VALUE
None
实际时间将取决于 func()
和 func2()
函数执行的操作。不是最好的例子,但下面给出了一个快速(和肮脏的)测试代码:
import time
def func(x):
return 5
def func2(a):
return 10
t0 = time.time()
x = 10
for i in range(1,10000):
a = func(x)
b = func2(a)
t1 = time.time()
print("Time 1: ", t1-t0)
t2 = time.time()
x = 10
for i in range(1,10000):
b = func2(func(x))
t3 = time.time()
print("Time 2: ", t3-t2)
以上代码的输出为:
Time 1: 0.0029211044311523438
Time 2: 0.002785921096801758
所以是的,我们避免分配 a
的实现在 Pyhton 3 中稍微快一些。