给定一个生成器
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 个元素之前迭代 k-1 个元素。谢谢。
编辑:这是惰性列表类的快照:
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 直到它到达键的第一个元素(或者如果键是一个切片,则使用 itertool 的 islice)。还有常见的 llmap、llreduce 等函数,我已经省略了,但您可能会猜到它们是如何进行的。
我希望能够分解生成器的动机之一是我可以像这样优雅地初始化这个类
l = LazyList(x*x for x in range(10))
代替
l = LazyList(range(10), lambda x: x*x)
但真正的好处是,经过润色,这将是生成器概念的一个很好的概括,并且能够用于代替任何生成器(具有相同的内存节省优势)。
我在 Django 中经常使用它,因为它与他们的查询集配合得很好。我有很多依赖于惰性列表结构的代码,因为它返回多维数组,如果对其进行评估,将获取比我需要的更多的数据。
请您参考如下方法:
我能想到的最接近的是反汇编生成器表达式中的代码对象。有点像
>>> 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 — 也许您可以使用它来获得更易于阅读的内容。
