给定一个发电机
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一起使用它很多,因为它适用于他们的查询集。我有很多代码依赖于列表结构是懒惰的,因为它返回多维数组,如果进行评估,它将获取比我需要的数据更多的数据。
答案 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个