Python 代码放在函数中运行比放在全局中运行快
2017-04-11 19:48
330 查看
作者:RednaxelaFX
链接:https://www.zhihu.com/question/44011219/answer/96809146
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
因为CPython的解释器实现细节。
CPython的解释器,对局部变量是用数组存储,用下标来访问;而对全局变量是用dict来存储,用符号(symbol)来做hash访问。速度差距是杠杠的。
这是因为一旦函数定义好之后,局部变量的个数就不能改了,所以可以用固定大小的容器存储;而全局名字是可以一边执行一边改变的,所以得用更动态的方式来存储。
看CPython的字节码的 LOAD_FAST 与 LOAD_GLOBAL 就可以看出差异了 ^_^
对上面的描述觉得迷惑的同学,可以先参考一些背景资料:有没有内容类似于《Python源码剖析》,但内容更新过,针对新版本的Python书籍? - RednaxelaFX 的回答
特别是其中用Python实现的教学用CPython字节码解释器的例子:
A Python Interpreter Written in Python(其代码在
byterun/pyvm2.py at master · nedbat/byterun · GitHub)
这个用Python实现的解释器其中就有这里提到的读写局部变量用的 LOAD_FAST / STORE_FAST 指令的实现——但为了简单起见,它实现这两条字节码指令是用 dict 来存储数据的,而不是像真正的CPython那样用数组来存储。其实稍微改改这个Python代码就可以让它在这方面更接近CPython的样子了。
作者:RednaxelaFX
链接:https://www.zhihu.com/question/44011219/answer/96809146
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
===========================================
评论区有同学提到 locals(),这在正常Python里是改变不了实际局部变量的状态的喔:
然后也有提到exec / eval的:
注意这里“局部变量”c是在一个exec语句(Python 3的话是exec()函数)里动态定义的,而在exec语句后对c的使用就用的是LOAD_NAME字节码而不是普通局部变量用的LOAD_FAST——这说明了动态定义的局部变量与普通局部变量的差异,而前面说“局部变量的个数不会改变”不包括这种动态定义的情况。
链接:https://www.zhihu.com/question/44011219/answer/96809146
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
因为CPython的解释器实现细节。
CPython的解释器,对局部变量是用数组存储,用下标来访问;而对全局变量是用dict来存储,用符号(symbol)来做hash访问。速度差距是杠杠的。
这是因为一旦函数定义好之后,局部变量的个数就不能改了,所以可以用固定大小的容器存储;而全局名字是可以一边执行一边改变的,所以得用更动态的方式来存储。
看CPython的字节码的 LOAD_FAST 与 LOAD_GLOBAL 就可以看出差异了 ^_^
对上面的描述觉得迷惑的同学,可以先参考一些背景资料:有没有内容类似于《Python源码剖析》,但内容更新过,针对新版本的Python书籍? - RednaxelaFX 的回答
特别是其中用Python实现的教学用CPython字节码解释器的例子:
A Python Interpreter Written in Python(其代码在
byterun/pyvm2.py at master · nedbat/byterun · GitHub)
这个用Python实现的解释器其中就有这里提到的读写局部变量用的 LOAD_FAST / STORE_FAST 指令的实现——但为了简单起见,它实现这两条字节码指令是用 dict 来存储数据的,而不是像真正的CPython那样用数组来存储。其实稍微改改这个Python代码就可以让它在这方面更接近CPython的样子了。
作者:RednaxelaFX
链接:https://www.zhihu.com/question/44011219/answer/96809146
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
===========================================
评论区有同学提到 locals(),这在正常Python里是改变不了实际局部变量的状态的喔:
$ python Python 2.7.5 (default, Mar 9 2014, 22:15:05) [GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> def foo(): ... a = 1 ... b = 2 ... locals()['a'] = 42 ... print(a, b) ... >>> foo() (1, 2) >>> import dis >>> dis.dis(foo) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (a) 3 6 LOAD_CONST 2 (2) 9 STORE_FAST 1 (b) 4 12 LOAD_CONST 3 (42) 15 LOAD_GLOBAL 0 (locals) 18 CALL_FUNCTION 0 21 LOAD_CONST 4 ('a') 24 STORE_SUBSCR 5 25 LOAD_FAST 0 (a) 28 LOAD_FAST 1 (b) 31 BUILD_TUPLE 2 34 PRINT_ITEM 35 PRINT_NEWLINE 36 LOAD_CONST 0 (None) 39 RETURN_VALUE
然后也有提到exec / eval的:
>>> def bar(): ... a = 1 ... b = 2 ... exec('c = 3') ... print(a, b, c) ... print(locals()) ... >>> bar() (1, 2, 3) {'a': 1, 'c': 3, 'b': 2} >>> dis.dis(bar) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (a) 3 6 LOAD_CONST 2 (2) 9 STORE_FAST 1 (b) 4 12 LOAD_CONST 3 ('c = 3') 15 LOAD_CONST 0 (None) 18 DUP_TOP 19 EXEC_STMT 5 20 LOAD_FAST 0 (a) 23 LOAD_FAST 1 (b) 26 LOAD_NAME 0 (c) 29 BUILD_TUPLE 3 32 PRINT_ITEM 33 PRINT_NEWLINE 6 34 LOAD_NAME 1 (locals) 37 CALL_FUNCTION 0 40 PRINT_ITEM 41 PRINT_NEWLINE 42 LOAD_CONST 0 (None) 45 RETURN_VALUE
注意这里“局部变量”c是在一个exec语句(Python 3的话是exec()函数)里动态定义的,而在exec语句后对c的使用就用的是LOAD_NAME字节码而不是普通局部变量用的LOAD_FAST——这说明了动态定义的局部变量与普通局部变量的差异,而前面说“局部变量的个数不会改变”不包括这种动态定义的情况。
相关文章推荐
- python为什么函数里的代码会比全局书写运行更快
- python获取当前运行函数名称的方法实例代码
- Python字典,函数,全局变量代码解析
- python_获取当前代码行号_获取当前运行的类名和函数名的方法
- C++程序运行时内存布局之----------局部变量,全局变量,静态变量,函数代码,new出来的变量
- Python字典,函数,全局变量代码解析
- python计算一段代码的运行时间(类和函数)
- C++程序运行时内存布局之----------局部变量,全局变量,静态变量,函数代码,new出来的变量
- C++程序运行时内存布局之----------局部变量,全局变量,静态变量,函数代码,new出来的变量
- Run As(运行方式) 的几种代码实现方式(Python和C#)
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码
- Flex全局错误处理Global Error Handler代码兼容运行于低版本Flash Player
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。
- 尽量避免使用全局变量,以防与其它函数重名影响运行结果
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起
- 正试图在 os 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起
- 正试图在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。
- java代码运行中获取正被调用的函数名
- 移植代码必看-C 运行时函数的 Win 32 等效