我想将生成器或迭代器转换为递归列表。
我在下面编写了一个代码,但是看起来很幼稚和丑陋,在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()

最佳答案

为了避免严重影响原始对象,您基本上需要对copy.deepcopy ...进行一些微调,因为您需要将生成器和迭代器转换为列表(deepcopy不会对生成器进行深拷贝)。请注意,不幸的是,对原始对象的某些影响是不可避免的,因为生成器和迭代器是“穷尽”的,这是对它们进行一路迭代的副作用(无论是将其转换为列表还是出于其他目的)–因此,简直是无法同时保留原始对象并使生成器或其他迭代器变成“variant-deepcopied”结果中的列表的方式。

不幸的是,copy模块没有被编写为自定义的,因此替代方案是,复制粘贴编辑,或者在私有(private)模块变量_deepcopy_dispatch上(双叹)微妙(叹息)的猴子补丁铰链(这意味着您的补丁版本可能无法从Python版本升级(假设从2.6升级到2.7)生存下来)。另外,每次使用eagerize后都必须卸载Monkey-patch(以避免影响deepcopy的其他用途)。因此,假设我们选择了复制粘贴编辑路径。

假设我们从最新版本开始,即在线here。当然,您需要重命名模块;在第145行将外部可见函数deepcopy重命名为eagerize;实质性的更改是在第161-165行,在上述版本中,其注释为:

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

我们需要在第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:

仅此而已:仅添加了两行。请注意,我将原始行号留给了自己,以使其完全清楚地将这两行插入到正确的位置,而没有对这两行进行编号。您还需要将标识符deepcopy(间接递归调用)的其他实例重命名为eagerize

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

当然,您想要的是纯文本版本copy.pyhere的副本,而不是我一直在使用的带注释的版本(我使用带注释的版本只是为了明确需要更改的地方!-)。

10-07 21:56