Python 为什么循环遍历枚举比生成器快得多?

Python 为什么循环遍历枚举比生成器快得多?,python,optimization,generator,enumeration,Python,Optimization,Generator,Enumeration,最近,在线程中Jon Clements的帮助下,我发现以下代码的执行时间非常不同 你知道为什么会这样吗 注释:self.stream_data是一个包含许多零和int16值的向量元组,create_ZS_data方法正在执行所谓的零抑制 环境 输入:许多(3.5k)小文件(每个约120kb) 操作系统:Linux64 Python 2.6.8版 基于生成器的解决方案: def create_ZS_data(self): self.ZS_data = ( [column, row, sel

最近,在线程中Jon Clements的帮助下,我发现以下代码的执行时间非常不同

你知道为什么会这样吗

注释:self.stream_data是一个包含许多零和int16值的向量元组,create_ZS_data方法正在执行所谓的零抑制

环境
输入:许多(3.5k)小文件(每个约120kb)
操作系统:Linux64
Python 2.6.8版

基于生成器的解决方案:

def create_ZS_data(self):
    self.ZS_data = ( [column, row, self.stream_data[column + row * self.rows ]]
                     for row, column in itertools.product(xrange(self.rows), xrange(self.columns))
                     if self.stream_data[column + row * self.rows ] )
create_ZS_data(self):
    self.ZS_data = list()
    for rowno, cols in enumerate(self.stream_data[i:i+self.columns] for i in xrange(0, len(self.stream_data), self.columns)):
        for colno, col in enumerate(cols):
            # col == value, (rowno, colno) = index
            if col:
                self.ZS_data.append([colno, rowno, col])
探查器信息:


探查器信息:


我看了之前的讨论;您似乎感到不安的是,您的聪明理解在循环中不如在源代码字符中有效。我当时没有指出的是,这将是我的首选实现:

def sparse_table_elements(cells, columns, rows):
    ncells = len(cells)
    non_zeros = list()
    for nrow in range(0, ncells, columns):
         row = cells[nrow:nrow+columns]
         for ncol, cell in enumerate(row):
             if cell:
                 non_zeros.append([ncol, nrow, cell])
    return non_zeros
我没有测试过,但我能理解。有几件事让我觉得可能是效率低下。重新计算两个常数单调“无聊”指数的笛卡尔乘积的代价一定很高:

itertools.product(xrange(self.rows), xrange(self.columns))
然后使用结果
[(0,0)、(0,1),…]
从源代码执行单元素索引:

stream_data[column + row * self.rows]
这也比像“Jon”实现那样处理更大的切片成本更高


发电机不是保证效率的秘方。在这个特殊的例子中,135kb的数据已经被读取到核心中,一个构造糟糕的生成器似乎确实会让您付出代价。如果您想要简洁的矩阵运算,请使用;如果您想要可读的代码,请不要在Python中追求极简化。

您可以简单地将Jon的解决方案重写为生成器:

def create_ZS_data(self):
    self.ZS_data = ([colno, rowno, col]
                    for rowno, cols in enumerate(self.stream_data[i:i+self.columns]
                                                 for i in xrange(0, len(self.stream_data), self.columns))
                    for colno, col in enumerate(cols)
                    if col)

我强烈期望这与Jon基于循环的解决方案的表现相同,证明性能的差异在于算法实现的中等规模细节。

很明显,这与第一个解决方案中的调用数有关,不是吗?是的,但发电机不是天生就可以处理大量电话吗?它们通常被推荐作为大列表的替代。对于简单的情况,生成器通常以CPU为代价来减少内存使用。您的第一个解决方案实际上不是使用生成器表达式,但是列表理解。@hop:列表理解包含一个生成器表达式,如所示的探查器信息中所述--
itertools.product()
是一个生成器函数。此外,“单元素切片”您所指的——我将简单地称之为对
流数据的索引访问
——实际上在generator/list comp版本中执行了两次。
stream_data[column + row * self.rows]
def create_ZS_data(self):
    self.ZS_data = ([colno, rowno, col]
                    for rowno, cols in enumerate(self.stream_data[i:i+self.columns]
                                                 for i in xrange(0, len(self.stream_data), self.columns))
                    for colno, col in enumerate(cols)
                    if col)