- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
哈喽大家好,我是咸鱼 。
当谈到编程效率和性能优化时,Python 常常被调侃为“慢如蜗牛” 。
有趣的是,Python 代码在函数中运行往往比在全局范围内运行要快得多 。
小伙伴们可能会有这个疑问:为什么在函数中运行的 Python 代码速度更快?
今天这篇文章将会解答大家心中的疑惑 。
原文链接: https://stackabuse.com/why-does-python-code-run-faster-in-a-function/ 。
要理解为什么 Python 代码在函数中运行得更快,我们需要首先了解 Python 是如何执行代码的 。
我们知道,python 是一种解释型语言,它会逐行读取并执行代码 。
当运行一个 python 程序的时候,首先将代码编译成字节码(一种更接近机器码的中间语言)然后 python 解释器执行字节码 。
def hello_world():
print("Hello, World!")
import dis
dis.dis(hello_world)
#结果
2 0 LOAD_GLOBAL 0 (print)
2 LOAD_CONST 1 ('Hello, World!')
4 CALL_FUNCTION 1
6 POP_TOP
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
由上所示,python 中的 dis 模块将函数 hello_world 分解为字节码 。
需要注意的是,python 解释器是一个执行字节码的虚拟机,默认的 python 解释器是用 C 编写的,即 CPython 。
还有其他的 python 解释器如 Jython(用 Java 编写),IronPython(用于 .net)和PyPy(用 Python 和 C 编写) 。
为什么 Python 代码在函数中运行得更快 。
我们来编写一个简单的例子:定义一个函数 my_function ,函数内部包含一个 for 循环 。
def my_function():
for i in range(100000000):
pass
编译该函数的时候,字节码可能如下所示 。
SETUP_LOOP 20 (to 23)
LOAD_GLOBAL 0 (range)
LOAD_CONST 3 (100000000)
CALL_FUNCTION 1
GET_ITER
FOR_ITER 6 (to 22)
STORE_FAST 0 (i)
JUMP_ABSOLUTE 13
POP_BLOCK
LOAD_CONST 0 (None)
RETURN_VALUE
这里的关键指令是 STORE_FAST ,用于存储循环变量 i 。
现在我们把这个 for 循环放在 python 脚本的顶层(全局范围内),然后再来看一下字节码 。
for i in range(100000000):
pass
SETUP_LOOP 20 (to 23)
LOAD_NAME 0 (range)
LOAD_CONST 3 (100000000)
CALL_FUNCTION 1
GET_ITER
FOR_ITER 6 (to 22)
STORE_NAME 1 (i)
JUMP_ABSOLUTE 13
POP_BLOCK
LOAD_CONST 2 (None)
RETURN_VALUE
可以看到关键指令变成了 STORE_NAME ,而不是 STORE_FAST 。
字节码 STORE_FAST 比 STORE_NAME 快, 因为在函数中,局部变量存储在固定长度的数组中,而不是存储在字典中。这个数组可以通过索引直接访问,使得变量检索非常快 。
基本上,它只是一个指向列表的指针,并增加了 PyObject 的引用计数,这两个都是高效的操作 。
另一方面,全局变量存储在一个字典。当访问全局变量时,Python 必须执行哈希表查找,这涉及计算哈希值,然后检索与之关联的值 。
虽然经过优化,但仍然比基于索引的查找慢 。
基准测试验证 。
我们知道在 Python 中,代码执行的速度取决于代码执行的位置——在函数中还是在全局作用域中 。
让我们用一个简单的基准测试的例子来比较一下 。
首先定义一个求阶乘的函数 。
def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result
然后在全局范围内执行相同的代码 。
n = 20
result = 1
for i in range(1, n + 1):
result *= i
为了对这两段代码进行基准测试,我们可以在 Python 中使用 timeit 模块,它提供了一种简单的方法来对少量 Python 代码进行计时 。
import timeit
# 函数
def benchmark():
start = timeit.default_timer()
factorial(20)
end = timeit.default_timer()
print(end - start)
benchmark()
# Prints: 3.541994374245405e-06
# 全局范围
start = timeit.default_timer()
n = 20
result = 1
for i in range(1, n + 1):
result *= i
end = timeit.default_timer()
print(end - start)
# Pirnts: 5.375011824071407e-06
可以看到,函数代码的执行速度比全局作用域代码要快 。
需要注意的是,这两段代码最好不要放在同一脚本中,要分开单独运行 。
这是因为 benchmark() 函数在执行时间上增加了一些开销,并且全局代码在内部进行了优化 。
cProfile 分析 。
python 提供了一个 cProfile 内置模块 。
让我们用它来分析一个新例子:在局部和全局范围内计算平方和 。
import cProfile
def sum_of_squares():
total = 0
for i in range(1, 10000000):
total += i * i
i = None
total = 0
def sum_of_squares_g():
global i
global total
for i in range(1, 10000000):
total += i * i
def profile(func):
pr = cProfile.Profile()
pr.enable()
func()
pr.disable()
pr.print_stats()
#
# Profile function code
#
print("Function scope:")
profile(sum_of_squares)
#
# Profile global scope code
#
print("Global scope:")
profile(sum_of_squares_g)
上面的例子中,可以认为 sum_of_squares_g() 函数是全局的,因为它使用了两个全局变量, i 和 total 。
从性能分析结果中,可以看到函数代码在执行时间方面比全局更有效 。
Function scope:
2 function calls in 0.903 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.903 0.903 0.903 0.903 profiler.py:3(sum_of_squares)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Global scope:
2 function calls in 1.358 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 1.358 1.358 1.358 1.358 profiler.py:10(sum_of_squares_g)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
如何优化 python 函数的性能 。
前面我们知道,Python 代码在函数中运行往往比在全局范围内运行要快得多 。
如果想要进一步提高 python 函数代码效率,不妨考虑一下 使用局部变量而不是全局变量 。
另一种方法是尽可能 使用内置函数和库 。Python 的内置函数是用 C 实现的,比 Python 快得多 。
比如 NumPy 和 Pandas,也是用 C 或 C++ 实现的,它们比实现同样功能的 Python 代码速度更快 。
又比如同样是实现数字求和的功能,python 内置的 sum 函数要比你自己编写函数速度更快 。
最后此篇关于为什么Python代码在函数中运行得更快?的文章就讲到这里了,如果你想了解更多关于为什么Python代码在函数中运行得更快?的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我需要将文本放在 中在一个 Div 中,在另一个 Div 中,在另一个 Div 中。所以这是它的样子: #document Change PIN
奇怪的事情发生了。 我有一个基本的 html 代码。 html,头部, body 。(因为我收到了一些反对票,这里是完整的代码) 这是我的CSS: html { backgroun
我正在尝试将 Assets 中的一组图像加载到 UICollectionview 中存在的 ImageView 中,但每当我运行应用程序时它都会显示错误。而且也没有显示图像。 我在ViewDidLoa
我需要根据带参数的 perl 脚本的输出更改一些环境变量。在 tcsh 中,我可以使用别名命令来评估 perl 脚本的输出。 tcsh: alias setsdk 'eval `/localhome/
我使用 Windows 身份验证创建了一个新的 Blazor(服务器端)应用程序,并使用 IIS Express 运行它。它将显示一条消息“Hello Domain\User!”来自右上方的以下 Ra
这是我的方法 void login(Event event);我想知道 Kotlin 中应该如何 最佳答案 在 Kotlin 中通配符运算符是 * 。它指示编译器它是未知的,但一旦知道,就不会有其他类
看下面的代码 for story in book if story.title.length < 140 - var story
我正在尝试用 C 语言学习字符串处理。我写了一个程序,它存储了一些音乐轨道,并帮助用户检查他/她想到的歌曲是否存在于存储的轨道中。这是通过要求用户输入一串字符来完成的。然后程序使用 strstr()
我正在学习 sscanf 并遇到如下格式字符串: sscanf("%[^:]:%[^*=]%*[*=]%n",a,b,&c); 我理解 %[^:] 部分意味着扫描直到遇到 ':' 并将其分配给 a。:
def char_check(x,y): if (str(x) in y or x.find(y) > -1) or (str(y) in x or y.find(x) > -1):
我有一种情况,我想将文本文件中的现有行包含到一个新 block 中。 line 1 line 2 line in block line 3 line 4 应该变成 line 1 line 2 line
我有一个新项目,我正在尝试设置 Django 调试工具栏。首先,我尝试了快速设置,它只涉及将 'debug_toolbar' 添加到我的已安装应用程序列表中。有了这个,当我转到我的根 URL 时,调试
在 Matlab 中,如果我有一个函数 f,例如签名是 f(a,b,c),我可以创建一个只有一个变量 b 的函数,它将使用固定的 a=a1 和 c=c1 调用 f: g = @(b) f(a1, b,
我不明白为什么 ForEach 中的元素之间有多余的垂直间距在 VStack 里面在 ScrollView 里面使用 GeometryReader 时渲染自定义水平分隔线。 Scrol
我想知道,是否有关于何时使用 session 和 cookie 的指南或最佳实践? 什么应该和什么不应该存储在其中?谢谢! 最佳答案 这些文档很好地了解了 session cookie 的安全问题以及
我在 scipy/numpy 中有一个 Nx3 矩阵,我想用它制作一个 3 维条形图,其中 X 轴和 Y 轴由矩阵的第一列和第二列的值、高度确定每个条形的 是矩阵中的第三列,条形的数量由 N 确定。
假设我用两种不同的方式初始化信号量 sem_init(&randomsem,0,1) sem_init(&randomsem,0,0) 现在, sem_wait(&randomsem) 在这两种情况下
我怀疑该值如何存储在“WORD”中,因为 PStr 包含实际输出。? 既然Pstr中存储的是小写到大写的字母,那么在printf中如何将其给出为“WORD”。有人可以吗?解释一下? #include
我有一个 3x3 数组: var my_array = [[0,1,2], [3,4,5], [6,7,8]]; 并想获得它的第一个 2
我意识到您可以使用如下方式轻松检查焦点: var hasFocus = true; $(window).blur(function(){ hasFocus = false; }); $(win
我是一名优秀的程序员,十分优秀!