恢复python生成器的定义表达式

时间:2012-01-14 18:56:34

标签: python generator list-comprehension

给定一个发电机

g = ( <expr> for x in <iter> ),

有没有办法恢复用于定义g的表达式和迭代器?

,例如,一个表现如下的函数:

expr, iter = f( ( x*x for x in range(10) ) )
expr(2) # 4
expr(5) # 25
iter[1] # 1
iter[9] # 9
iter[10] # raises IndexError

我想要这个功能的原因是我已经创建了自己的LazyList类。我希望它基本上像一个生成器,除了允许通过 getitem 访问,而不必迭代k-1元素,然后才能访问第k个元素。感谢。

编辑:这是懒惰列表类的快照:

class LazyList(object):
  def __init__(self, iter=None, expr=None):
    if expr is None:
      expr = lambda i: i
    if iter is None:
      iter = []
    self._expr = expr
    self._iter = iter

  def __getitem__(self, key):
    if hasattr(self._iter, '__getitem__'):
      return self._expr(self._iter[key])
    else:
      return self._iter_getitem(key)

  def __iter__(self):
    for i in self._iter:
      yield self._expr(i)

我省略了方法_iter_getitem。所有这一切都是迭代_iter直到它到达key'th元素(或者如果key是切片则使用itertool的islice)。我还省略了常见的llmap,llreduce等功能,但你可能会猜到它们是怎么回事。

我希望能够分解生成器的动机之一就是我可以优雅地初始化这个类,如

l = LazyList(x*x for x in range(10))

而不是

l = LazyList(range(10), lambda x: x*x)

但真正的好处是,通过抛光,这将是发电机概念的一个很好的概括,并且能够用于代替任何发电机(具有相同的存储器节省益处)。

我和Django一起使用它很多,因为它适用于他们的查询集。我有很多代码依赖于列表结构是懒惰的,因为它返回多维数组,如果进行评估,它将获取比我需要的数据更多的数据。

2 个答案:

答案 0 :(得分:1)

我能想到的最接近的是反汇编生成器表达式中的代码对象。像

这样的东西
>>> import dis
>>> g = ( x*x for x in range(10) )
>>> dis.dis(g.gi_code)
  1           0 LOAD_FAST                0 (.0)
        >>    3 FOR_ITER                15 (to 21)
              6 STORE_FAST               1 (x)
              9 LOAD_FAST                1 (x)
             12 LOAD_FAST                1 (x)
             15 BINARY_MULTIPLY     
             16 YIELD_VALUE         
             17 POP_TOP             
             18 JUMP_ABSOLUTE            3
        >>   21 LOAD_CONST               0 (None)
             24 RETURN_VALUE        

这提供了一些关于发生了什么的暗示,但是不太清楚,恕我直言。

another Stack Overflow question处理将Python字节代码转换为可读的Python - 也许你可以用它来获得更易读的东西。

答案 1 :(得分:0)

我认为你的LazyList概念很好,但是你想要直接访问生成器的第n个值是有缺陷的。使用range(10)作为迭代序列的示例是一种特殊情况,其中所有值都可以提前知道。但是,许多生成器是递增计算的,其中第n个值是基于第n-1个值计算的。斐波那契生成器就是这样的:

def fibonacci(n=1000):
    a,b=1,1
    yield a
    while n>0:
        n -= 1
        yield b
        a,b = b,a+b

这给出了熟悉的系列1,1,2,3,5,8 ......,其中第n个项是n-1&n-th和n-2&#39的总和。 ;位。所以没有办法直接跳到第10项,你必须通过0-9项目到达那里。

话虽这么说,你的LazyList很好,有几个原因:

  • 它允许您重新访问早期值

  • 它模拟直接访问,即使在封面下,生成器必须经历所有增量值,直到它到达&#39;

  • 它只计算实际需要的值,因为懒惰地评估了生成器,而不是先发制人地计算1000个值,只是为了找到前10个