python - 循环遍历可迭代类的开销

bow*_*bow 7 python performance benchmarking

我正在摆弄Python的生成器和可迭代类,只是为了好玩.基本上我想测试一些我从来不太确定的东西:Pythons中的类有一些显着的开销,如果可以的话,最好依赖实现的方法yield而不是实现迭代器协议的类.

我无法找到有关此主题的谷歌一个令人满意的解释,所以我决定用这两个简单的脚本来测试他们对我自己:func_iter.pyclass_iter.py

这是func_iter.py:

#!/usr/bin/env python

import time  

x = 0
def create_generator(num):
    mylist = range(num)
    for i in mylist:
        yield i

t = time.time()
gen = create_generator(100000)

for i in gen:
    x = x + i

print "%.3f" % (time.time() - t)
Run Code Online (Sandbox Code Playgroud)

这是class_iter.py:

#!/usr/bin/env python

import time

x = 0

class Generator(object):

    def __init__(self, num):
        self.start = 0
        self.end = num

    def __iter__(self):
        return self

    def next(self):
        if self.start == self.end:
            raise StopIteration
        else:
            self.start = self.start + 1
            return self.start

t = time.time()
gen = Generator(100000)

for i in gen:
    x = x + i

print "%.3f" % (time.time() - t)
Run Code Online (Sandbox Code Playgroud)

然后我在bash中使用它运行10次(class_iter.py例如):

for i in {1..10}; do ./class_iter.py; done
Run Code Online (Sandbox Code Playgroud)

以下是每个人的平均运行时间:

class_iter.py: 0.0864
func_iter.py: 0.0307
Run Code Online (Sandbox Code Playgroud)

现在,我的问题是:

  1. 我的方法是否正确?我的比较公平吗?
  2. 如果是这样,为什么差别很大?为什么要跑class_iter.py几乎三倍func_iter.py
  3. 如果没有,我如何改进我的方法或提出更好的比较?

编辑:正如Dacav所说,我也试过func_iter.pyxrange而不是运行range.这将其平均运行时间减少到0.0263秒.

geo*_*org 6

类版本花费大量时间访问自己的变量.每个self.whatever成本周期.如果将您定义__iter__为生成器并最小化实例变量的使用,则类和函数版本之间的差异可以忽略不计:

setup = """
def create_generator(num):
    mylist = range(num)
    for i in mylist:
        yield i

class Generator(object):

    def __init__(self, num):
        self.start = 0
        self.end = num

    def __iter__(self):
        return self

    def next(self):
        if self.start == self.end:
            raise StopIteration
        else:
            self.start = self.start + 1
            return self.start

class Generator2(object):

    def __init__(self, num):
        self.mylist = range(num)

    def __iter__(self):
        for i in self.mylist:
            yield i
"""

import timeit

print timeit.timeit('for p in create_generator(1000):p', setup, number=1000)
print timeit.timeit('for p in Generator(1000):p', setup, number=1000)
print timeit.timeit('for p in Generator2(1000):p', setup, number=1000)
Run Code Online (Sandbox Code Playgroud)

结果:

0.158941984177
0.696810007095
0.160784959793
Run Code Online (Sandbox Code Playgroud)

所以第二个生成器类几乎与函数版本一样快.

请注意,GeneratorGenerator2在本例中是不能完全等同的,有这样的情况,当你不能简单地替换发电机(如封送)一个"普通"的迭代器.