Python循环快于一行理解:为什么?

Python循环快于一行理解:为什么?,python,performance,Python,Performance,下面是两个完全相同的函数(将数字的阶乘相加)。 数组F包含0!。。。9! N = 1_000_000 F = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880] def facdigits1(n): total = 0 for i in map(int, str(n)): total += F[i] return total def facdigits2(n): return sum((F[i] f

下面是两个完全相同的函数(将数字的阶乘相加)。 数组F包含0!。。。9!

N = 1_000_000

F = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]

def facdigits1(n):
    total = 0
    for i in map(int, str(n)):
        total += F[i]
    return total

def facdigits2(n):
    return sum((F[i] for i in map(int, str(n))))

total = 0
for i in range(2, N):
    n = facdigits1(i)
    total += n
print(total)

完全出乎意料(也就是说,对我来说),facdigits2比facdigits1慢20%左右。我的预期正好相反。知道这是怎么回事吗?

问题本身隐藏在生成器上的
sum
函数性能稍慢的地方。通过反复寻找一些答案,我得出结论:生成器不会一次性将所有元素存储在内存中。以下是我定义总和的方法:

def s1(size):
    total = 0
    for i in range(size):
        total += i
    return total

def s2(size):
    return sum(i for i in range(size))

def s3(size):
    return sum(range(size))

def s4(size):
    def gen(size):
        for i in range(size):
            yield i
    return sum(gen(size))

def s5(size):
    def gen(size):
        i=0
        while i<size:
            yield i
            i+=1
    return sum(gen(size))

from time import time
import numpy as np

time_avg=[]
size = 10000000
for i in range(10):
    times=[]
    for s in [s1,s2,s3,s4,s5]:
        t = time()
        s(size)
        times.append(round(time()-t, 3))
    time_avg.append(times)

print(np.sum(np.array(time_avg), axis=0)/len(time_avg))

安装开销似乎更高。以下是用三种方法求和的
2**25/n
次数的秒数(因此,对于每n行,求和的总数是相同的,即
2**25
)。请注意,这三种启动方式都很慢,而环路启动速度最快。不知道为什么loop在低于2.0之后会上升到2.8(编辑:可能会使低于257的总和更快,但其他两种解决方案似乎不会受到影响)

你有≤ 6,所以我的测试结果表明发电机的速度较慢。在我的例子中,它更极端,大概是因为我没有做任何其他事情,比如将数字转换为字符串、查找列表索引和对大数字求和

代码:


你是如何测试速度的?前1000个数字的内环。顺便说一句,在我的Mac和覆盆子4上也是这样。谢谢你的邀请!我只是试了一下:下面。我怀疑生成器对象需要某种内部函数调用才能获得下一个值>>>(map(int,str(100))中I的F[I])@user2740650是的,这就是使用生成器的意义所在。你能添加完整的测试代码以及
F
的定义吗?但这非常令人失望。我的意思是,相比之下,s2会被认为比s1更像蟒蛇,对吗?作为一个业余爱好者,我喜欢这样的想法:在s2中,求和是用本机代码而不是python循环完成的,而且速度较慢?来吧不知道你是怎么知道时间的。使用%timeit,我在1000个元素列表中得到:S1 S2 S4:60 us和S3:20 us。你应该试着改变。我不习惯%timeit,我只是用time.time()测量一次时间。我发现我的结果不太正确。我要更新,取更多测量值的平均值。@mathfux仅测量一次不会给出准确的结果。timeit为您提供多次运行的平均值和std(10 000到1 000 000,取决于函数到时间)。
[0.9624 1.3263 0.6373 1.2426 1.8455]
       n   loop   generator  pure
-----------------------------------
       1  11.3239  24.6003  12.5587
       2   6.8343  13.6080   5.6892
       4   4.4053   7.9283   3.0991
       8   3.2007   5.5467   1.8115
      16   2.5485   3.7965   1.1166
      32   2.2361   3.0682   0.7247
      64   2.0101   3.0527   0.5513
     128   2.0220   2.4149   0.4475
     256   1.8081   2.3848   0.3966
     512   2.0703   2.3418   0.4229
    1024   2.2276   2.2966   0.3706
    2048   2.2751   2.2856   0.3614
    4096   2.2761   2.3269   0.3533
    8192   2.3085   2.3623   0.3671
   16384   2.3395   2.2970   0.3552
   32768   2.4804   2.3583   0.4097
   65536   2.5998   2.3151   0.3572
  131072   2.7952   2.2654   0.3954
  262144   2.7445   2.4830   0.3599
  524288   2.7740   2.3462   0.3565
 1048576   2.7449   2.2845   0.3592
 2097152   2.8187   2.2246   0.3708
 4194304   2.8307   2.2691   0.3572
 8388608   2.8036   2.2731   0.3690
16777216   2.7704   2.3388   0.3649
33554432   2.8328   2.3018   0.3545
from timeit import timeit

def sum_loop(numbers):
    total = 0
    for number in numbers:
        total += number
    return total

def sum_generator(numbers):
    return sum(number for number in numbers)

def sum_pure(numbers):
    return sum(numbers)

emax = 25
for e in range(emax + 1):
    n = 2**e
    number = 2**(emax - e)
    numbers = [1] * n
    print('%8d' % n, end='')
    for f in sum_loop, sum_generator, sum_pure:
        t = timeit(lambda: f(numbers), number=number)
        print('  %7.4f' % t, end='')
    print()