与多行循环相比,单行循环是否节省了python代码的运行时间?

与多行循环相比,单行循环是否节省了python代码的运行时间?,python,performance,loops,Python,Performance,Loops,我想知道如何减少程序的运行时间。使用单线循环比多线循环更有效吗 #is this more efficient total_data = [[arr1[j][i] for j in range(3)]+ arr2[i][0] for i in range(10000)] #instead of total_data = [] for i in range(10000): arr3 = [] a2 = arr2[i][0] for j in range(3):

我想知道如何减少程序的运行时间。使用单线循环比多线循环更有效吗

#is this more efficient

total_data = [[arr1[j][i] for j in range(3)]+ arr2[i][0] for i in range(10000)]

#instead of

total_data = []
for i in range(10000):
    arr3 = []
    a2 = arr2[i][0]
    for j in range(3):
        arr3.append(arr1[j][i])
    total_data.append(arr3+a2)
另外,在调用函数时,使用map是否比for循环节省更多时间

#this

f1 = map(func1, var1, var2)
arr = map(func2, f2, var3, var4)

#instead of this

arr = []
for i in range(1000):
    f1 = func1(var1(i), var2(i))
    f2 = func2(f1(i))
    arr.append(f2, var3, var4)

我的数据集很大,每个函数的运行时间也相当长,因此我希望尽可能减少时间。我想从根本上知道,在python中为同一个循环增加行数是否会增加时间。

更多的代码行是否意味着更少的性能这一问题是有缺陷的,因为无论是
还是
更少的行数≠ 演出≠ 更多行
。性能将取决于Python代码如何解析为执行它的底层C函数调用,这主要是解析器/解释器的实现细节,没有与行数的一对一等价

也就是说,在选择循环vs列表理解vs映射时,需要考虑以下几点

  • 当代码的目的是创建一个列表时,列表理解通常比循环快。AFAIK这与对append方法的第二次do调用有关,还因为增量列表构造可能会导致列表调整大小,从而导致一些内存重新分配开销

  • 无论调用python函数的位置如何,调用python函数都是一个缓慢的操作。引用如下:
    函数调用需要操纵堆栈,将本地帧推到堆栈上,创建一个新帧,然后在函数返回时再次清除所有帧。
    其中一个例外是用C编写的函数,例如模块中的函数,它们比本机Python对应函数稍快

也就是说,我已经使用类似的代码进行了一些评测,测试表明循环是最慢的选项。有趣的是,测试还显示map比列表理解略快(与链接答案相比)。代码和结果如下:

ls = list(range(1000000))

def f(x):
    return x+1

def list_comprehension(ls):
    return [f(x) for x in ls]

def for_loop(ls):
    arr = []
    for x in ls:
        arr.append(f(x))
    return arr

def map_(ls):
    return list(map(f, ls))

if __name__ == "__main__":
    import cProfile
    for fn in [list_comprehension, for_loop, map_]:
        print('=' * 25)
        print("Profiling:", fn.__name__)
        print('=' * 25)
        pr = cProfile.Profile()
        for i in range(1000):
            pr.runcall(fn, ls)
        pr.create_stats()
        pr.print_stats()

# Output
=========================
Profiling: list_comprehension
=========================
         1000003000 function calls in 235.641 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1000000000  104.000    0.000  104.000    0.000 aa.py:5(f)
     1000    0.008    0.000  235.640    0.236 aa.py:8(list_comprehension)
     1000  131.632    0.132  235.632    0.236 aa.py:9(<listcomp>)
     1000    0.001    0.000    0.001    0.000 {method 'disable' of '_lsprof.Profiler' objects}


=========================
Profiling: for_loop
=========================
         2000002000 function calls in 410.884 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1000  242.083    0.242  410.883    0.411 aa.py:11(for_loop)
1000000000  107.727    0.000  107.727    0.000 aa.py:5(f)
1000000000   61.073    0.000   61.073    0.000 {method 'append' of 'list' objects}
     1000    0.001    0.000    0.001    0.000 {method 'disable' of '_lsprof.Profiler' objects}


=========================
Profiling: map_
=========================
         1000002000 function calls in 205.035 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1000  102.451    0.102  205.034    0.205 aa.py:17(map_)
1000000000  102.583    0.000  102.583    0.000 aa.py:5(f)
     1000    0.001    0.000    0.001    0.000 {method 'disable' of '_lsprof.Profiler' objects}
ls=列表(范围(1000000))
def f(x):
返回x+1
def列表(ls):
返回[f(x)表示ls中的x]
环路(ls)的def:
arr=[]
对于ls中的x:
arr.append(f(x))
返回arr
def映射(ls):
返回列表(地图(f、ls))
如果名称=“\uuuuu main\uuuuuuuu”:
导入cProfile
对于[列表\u理解,对于\u循环,映射\u]中的fn:
打印('='*25)
打印(“分析:”,fn.\u名称\u)
打印('='*25)
pr=cProfile.Profile()
对于范围(1000)内的i:
pr.runcall(fn,ls)
pr.create_stats()
公共印刷统计数据()
#输出
=========================
评测:列表
=========================
在235.641秒内调用100003000个函数
订购人:标准名称
ncalls tottime percall cumtime percall文件名:lineno(函数)
1000000000 104.000 0.000 104.000 0.000 aa.py:5(f)
1000 0.008 0.000 235.640 0.236 aa.py:8(列表)
1000 131.632 0.132 235.632 0.236 aa.py:9()
1000 0.001 0.000 0.001 0.000{方法'disable'的'lsprof.Profiler'对象}
=========================
评测:for_循环
=========================
在410.884秒内调用200002000个函数
订购人:标准名称
ncalls tottime percall cumtime percall文件名:lineno(函数)
1000 242.083 0.242 410.883 0.411 aa.py:11(用于_回路)
100000000 107.727 0.000 107.727 0.000 aa.py:5(f)
1000000000 61.073 0.000 61.073 0.000{“列表”对象的“附加”方法}
1000 0.001 0.000 0.001 0.000{方法'disable'的'lsprof.Profiler'对象}
=========================
剖析:地图_
=========================
在205.035秒内调用1000002000个函数
订购人:标准名称
ncalls tottime percall cumtime percall文件名:lineno(函数)
1000 102.451 0.102 205.034 0.205 aa.py:17(地图)
1000000000 102.583 0.000 102.583 0.000 aa.py:5(f)
1000 0.001 0.000 0.001 0.000{方法'disable'的'lsprof.Profiler'对象}

无论如何,在这种情况下,最好写下所有不同的版本,自己进行分析,而不是依赖于一般的假设。

行数与代码的执行完全无关。py文件将被读取、解析和编译一次。在其上,将执行编译后的代码

换句话说,如果您创建一个包含10GB空白的文件,是的,这将影响解析时间,但即使这样,执行也不会受到影响

两种变体之间的区别在于,一种变体使用列表理解创建列表,而另一种变体创建一个空列表,然后填充它。我的直觉是,列表理解理论上更容易优化,但如果你真的关心细微的差异,你应该对其进行分析


另一方面,执行时间的微小差异很少能证明代码看起来更糟糕,因此默认情况下,您应该始终选择更优雅的解决方案。

一行程序可能比更长的代码快,但这不是一般规则。有些python函数,如any(),all()允许非常紧凑的代码被优化以非常快地执行,并且可能比(例如)执行相同任务的两个嵌套循环更快。编译器/解释器并不真正关心需要处理多少文本(除非您的代码由数千行代码组成)。一个内衬是首选的,因为它节省了空间,使代码更加可读。在我看来,10000是一个小循环,不需要考虑单行或多行的效率。如果花费太多时间,可能芹菜就是解决办法。