如何将生成器或迭代器转换为递归列表

una*_*owa 7 python recursion iterator generator immutability

我想将生成器或迭代器转换为递归列表.
我在下面编写了一个代码,但它看起来很幼稚和丑陋,可能会在doctest中删除.

Q1.帮帮我好版本.
Q2.如何指定对象是不可变的?

import itertools

def isiterable(datum):
    return hasattr(datum, '__iter__')

def issubscriptable(datum):
    return hasattr(datum, "__getitem__")

def eagerlize(obj):
    """ Convert generator or iterator to list recursively.
    return a eagalized object of given obj.
    This works but, whether it return a new object, break given one.

    test 1.0 iterator

    >>> q = itertools.permutations('AB',  2)
    >>> eagerlize(q)
    [('A', 'B'), ('B', 'A')]
    >>>

    test 2.0 generator in list

    >>> q = [(2**x for x in range(3))]
    >>> eagerlize(q)
    [[1, 2, 4]]
    >>>

    test 2.1 generator in tuple

    >>> q = ((2**x for x in range(3)),)
    >>> eagerlize(q)
    ([1, 2, 4],)
    >>>

    test 2.2 generator in tuple in generator

    >>> q = (((x, (y for y in range(x, x+1))) for x in range(3)),)
    >>> eagerlize(q)
    ([(0, [0]), (1, [1]), (2, [2])],)
    >>>

    test 3.0 complex test

    >>> def test(r):
    ...     for x in range(3):
    ...         r.update({'k%s'%x:x})
    ...         yield (n for n in range(1))
    >>>
    >>> def creator():
    ...     r = {}
    ...     t = test(r)
    ...     return r, t
    >>>
    >>> a, b = creator()
    >>> q = {'b' : a, 'a' : b}
    >>> eagerlize(q)
    {'a': [[0], [0], [0]], 'b': {'k2': 2, 'k1': 1, 'k0': 0}}
    >>>

    test 3.1 complex test (other dict order)

    >>> a, b = creator()
    >>> q = {'b' : b, 'a' : a}
    >>> eagerlize(q)
    {'a': {'k2': 2, 'k1': 1, 'k0': 0}, 'b': [[0], [0], [0]]}
    >>>

    test 4.0 complex test with tuple

    >>> a, b = creator()
    >>> q = {'b' : (b, 10), 'a' : (a, 10)}
    >>> eagerlize(q)
    {'a': ({'k2': 2, 'k1': 1, 'k0': 0}, 10), 'b': ([[0], [0], [0]], 10)}
    >>>

    test 4.1 complex test with tuple (other dict order)

    >>> a, b = creator()
    >>> q = {'b' : (b, 10), 'a' : (a, 10)}
    >>> eagerlize(q)
    {'a': ({'k2': 2, 'k1': 1, 'k0': 0}, 10), 'b': ([[0], [0], [0]], 10)}
    >>>

    """
    def loop(obj):
        if isiterable(obj):
            for k, v in obj.iteritems() if isinstance(obj, dict) \
                         else enumerate(obj):
                if isinstance(v, tuple):
                    # immutable and iterable object must be recreate, 
                    # but realy only tuple?
                    obj[k] = tuple(eagerlize(list(obj[k])))
                elif issubscriptable(v):
                    loop(v)
                elif isiterable(v):
                    obj[k] = list(v)
                    loop(obj[k])

    b = [obj]
    loop(b)
    return b[0]

def _test():
    import doctest
    doctest.testmod()

if __name__=="__main__":
    _test()
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 5

为了避免严重影响原始对象,你基本上需要一个变量copy.deepcopy......巧妙地调整,因为你需要将生成器和迭代器转换成列表(深度复制不会深度复制生成器).请注意,遗憾的是,对原始对象的一些影响是不可避免的,因为生成器和迭代器被"耗尽"作为在它们上一直迭代的副作用(无论是将它们变成列表还是用于任何其他目的) - 因此,那里根本就没有办法可以独自离开原来的对象,并有发电机或其他迭代器变成了一个列表中的"变体deepcopied"的结果.

copy遗憾的是,该模块并未编写为自定义,因此可选择复制粘贴编辑或微妙(叹气)猴子补丁(双重叹息)私有模块变量_deepcopy_dispatch(这意味着您的补丁版本可能不会经过Python版本升级,假设从2.6到2.7,假设).另外,每次使用后都必须卸载猴子补丁eagerize(以避免影响其他用途deepcopy).所以,我们假设我们选择了复制粘贴编辑路线.

假设我们先从最近的版本,一个最在线这里.当然,您需要重命名模块; 重命名外部可见的功能deepcopy,以eagerize在线路145; 实质性的变化是在第161-165行,在所述版本中,注释为:

161 :               copier = _deepcopy_dispatch.get(cls)
162 :               if copier:
163 :                   y = copier(x, memo)
164 :               else:
165 :   tim_one 18729           try:
Run Code Online (Sandbox Code Playgroud)

我们需要在第163行和第164行之间插入逻辑"否则如果它可迭代将其扩展到列表(即,使用该函数_deepcopy_list作为复制器".所以这些行变为:

161 :               copier = _deepcopy_dispatch.get(cls)
162 :               if copier:
163 :                   y = copier(x, memo)
                     elif hasattr(cls, '__iter__'):
                         y = _deepcopy_list(x, memo)
164 :               else:
165 :   tim_one 18729           try:
Run Code Online (Sandbox Code Playgroud)

这就是全部:只有两条线.请注意,我独自离开原来的行号,使之清透,其中恰好这两行需要插入,并没有编号的两个新的生产线.您还需要将其他标识符实例deepcopy(间接递归调用)重命名为eagerize.

您还应该删除第66-144行(您不关心的浅拷贝功能)并适当调整第1-65行(文档字符串,导入__all__等).

当然,你想要处理明文版本的副本copy.py,这里不是我所指的注释版本(我使用带注释的版本只是为了明确需要更改的位置! - ).