总和是做什么的?

时间:2018-06-01 07:05:58

标签: python sum generator python-internals

首先,我想测试生成器和列表理解之间的内存使用情况。本书给了我一个小小的代码片段,我在我的PC上运行它(python3.6,Windows),找到意想不到的东西。

  1. 在书上,它说,因为列表理解必须创建一个真实的列表并为它分配内存,所以从列表理解中获取的速度必须比从生成器中获取更慢。
  2. 当然,list-comprehension使用的内存比生成器多。
  3. FOllowing是我的代码,它不满足以前的意见(在sum函数中)。

    import tracemalloc
    from time import time
    
    
    def timeIt(func):
        start = time()
        func()
        print('%s use time' % func.__name__, time() - start)
        return func
    
    
    tracemalloc.start()
    
    numbers = range(1, 1000000)
    
    
    @timeIt
    def lStyle():
        return sum([i for i in numbers if i % 3 == 0])
    
    
    @timeIt
    def gStyle():
        return sum((i for i in numbers if i % 3 == 0))
    
    
    lStyle()
    
    gStyle()
    
    shouldSize = [i for i in numbers if i % 3 == 0]
    
    snapshotL = tracemalloc.take_snapshot()
    top_stats = snapshotL.statistics('lineno')
    print("[ Top 10 ]")
    for stat in top_stats[:10]:
        print(stat)
    

    输出:

    lStyle use time 0.4460000991821289
    gStyle use time 0.6190001964569092
    [ Top 10 ]
    F:/py3proj/play.py:31: size=11.5 MiB, count=333250, average=36 B
    F:/py3proj/play.py:33: size=448 B, count=1, average=448 B
    F:/py3proj/play.py:22: size=136 B, count=1, average=136 B
    F:/py3proj/play.py:17: size=136 B, count=1, average=136 B
    F:/py3proj/play.py:14: size=76 B, count=2, average=38 B
    F:/py3proj/play.py:8: size=34 B, count=1, average=34 B
    

    两点:

    • 生成器使用更多时间和相同的内存空间。
    • sum函数中的list-comprehension似乎没有创建总列表

    我想也许sum函数做了我不知道的事情。谁能解释一下?

    这本书是High Perfoamance Python.chapter 5.但是我确实与本书不同,以检查其他背景下的有效性。他的代码在book_code,他并没有将列表理解放在总和函数中。

1 个答案:

答案 0 :(得分:2)

在时间性能测试方面,我确实依赖于timeit模块,因为它会自动执行多次代码运行。

在我的系统上,timeit给出了以下结果(由于大量运行,我大大减小了尺寸):

>>> timeit.timeit("sum([i for i in numbers if i % 3 == 0])", "numbers = range(1, 1000)")
59.54427594248068
>>> timeit.timeit("sum((i for i in numbers if i % 3 == 0))", "numbers = range(1, 1000)")
64.36398425334801

所以发电机慢了大约8%(*)。这实际上并不令人惊讶,因为生成器必须执行一些代码动态以获取下一个值,而预先计算的列表只增加其当前指针。

记忆评估恕我直言更复杂,所以我使用了来自activestate的Compute Memory footprint of an object and its contents (Python recipe)

>>> numbers = range(1, 100)
>>> numbers = range(1, 100000)
>>> l = [i for i in numbers if i % 3 == 0]
>>> g = (i for i in numbers if i % 3 == 0)
>>> total_size(l)
1218708
>>> total_size(g)
88
>>> total_size(numbers)
48

我的解释是列表使用内存来处理所有项目(这不是一个惊喜),而生成器只需要很少的值和一些代码,因此生成器的内存占用量要小得多。

我强烈认为您已将tracemalloc用于不适用的内容。它的目的是搜索可能的内存泄漏(大块内存从未解除分配),而不是控制单个对象使用的内存。

请注意:我只能测试小尺寸。但是对于非常大的大小,列表可能耗尽可用内存,并且机器将使用来自交换的虚拟内存。在这种情况下,列表版本将变得更慢。更多详情there