Я хочу преобразовать генератор или итератор для списка рекурсивно.
Я написал код в ниже, но это выглядит наивным и ужасным, и может быть закрытым делом в 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 не будет глубоко копировать генераторы в любом случае). Обратите внимание, что некоторый эффект на исходный объект, к сожалению, неизбежен, потому что генераторы и итераторы «исчерпаны» как побочный эффект полного итерации над ними (будь то превращение их в списки или для любой другой цели ) - следовательно, вы просто не можете как оставить исходный объект в покое , так и превратить этот генератор или другой итератор в список в результате с «глубоким копированием вариантов».
Модуль 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
, здесь , а не аннотированной версия, о которой я говорил (я использовал аннотированную версию, чтобы уточнить, где именно были необходимы изменения! -).