Как преобразовать генератор или итератор для списка рекурсивно

Я хочу преобразовать генератор или итератор для списка рекурсивно.
Я написал код в ниже, но это выглядит наивным и ужасным, и может быть закрытым делом в 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()
7
задан unacowa 5 July 2010 в 08:48
поделиться

1 ответ

Чтобы не повлиять на исходный объект, вам в основном понадобится вариант copy.deepcopy ... слегка измененный, потому что вам нужно превратить генераторы и итераторы в списки (deepcopy не будет глубоко копировать генераторы в любом случае). Обратите внимание, что некоторый эффект на исходный объект, к сожалению, неизбежен, потому что генераторы и итераторы «исчерпаны» как побочный эффект полного итерации над ними (будь то превращение их в списки или для любой другой цели ) - следовательно, вы просто не можете как оставить исходный объект в покое , так и превратить этот генератор или другой итератор в список в результате с «глубоким копированием вариантов».

Модуль 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:

Нам нужно вставить между строками 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 __ и т. Д.).

Of Конечно, вы хотите обработать копию открытого текста версии copy.py , здесь , а не аннотированной версия, о которой я говорил (я использовал аннотированную версию, чтобы уточнить, где именно были необходимы изменения! -).

5
ответ дан 7 December 2019 в 12:14
поделиться
Другие вопросы по тегам:

Похожие вопросы: