В последнее время я много работал с сопрограммами. Преимущество состоит в том, что вы уменьшаете накладные расходы на вызовы метода. Отправка нового значения в сопрограмму выполняется быстрее, чем вызов функции. В то время как вы не можете сделать рекурсивную сопрограмму, она выкинет ValueError: generator already executing
, но вы можете создать пул работников-сопродюсеров - вам нужен один рабочий для каждого уровня дерева. Я сделал некоторый тестовый код, который работает, но еще не рассмотрел вопросы синхронизации.
def coroutine(func):
""" A helper function decorator from Beazley"""
def start(*args, **kwargs):
g = func(*args, **kwargs)
g.next()
return g
return start
@coroutine
def cotuple2list():
"""This does the work"""
result = None
while True:
(tup, co_pool) = (yield result)
result = list(tup)
# I don't like using append. So I am changing the data in place.
for (i,x) in enumerate(result):
# consider using "if hasattr(x,'__iter__')"
if isinstance(x,tuple):
result[i] = co_pool[0].send((x, co_pool[1:]))
@coroutine
def colist2tuple():
"""This does the work"""
result = None
while True:
(lst, co_pool) = (yield result)
# I don't like using append so I am changing the data in place...
for (i,x) in enumerate(lst):
# consider using "if hasattr(x,'__iter__')"
if isinstance(x,list):
lst[i] = co_pool[0].send((x, co_pool[1:]))
result = tuple(lst)
Pure питон альтернатива от должности HYRY в:
def list2tuple(a):
return tuple((list2tuple(x) if isinstance(x, list) else x for x in a))
def tuple2list(a):
return list((tuple2list(x) if isinstance(x, tuple) else x for x in a))
Сделайте пул сопрограммам - это хак из бассейна, но это работает:
# Make Coroutine Pools
colist2tuple_pool = [colist2tuple() for i in xrange(20) ]
cotuple2list_pool = [cotuple2list() for i in xrange(20) ]
Теперь сделать некоторые сроки - по сравнению с:
def make_test(m, n):
# Test data function taken from HYRY's post!
return [[range(m), make_test(m, n-1)] for i in range(n)]
import timeit
t = make_test(20, 8)
%timeit list2tuple(t)
%timeit colist2tuple_pool[0].send((t, colist2tuple_pool[1:]))
Результаты - обратите внимание на «и» рядом с «S» во второй строке :-)
1 loops, best of 3: 1.32 s per loop
1 loops, best of 3: 4.05 us per loop
Действительно, кажется, слишком быстро, чтобы поверить. Кто-нибудь знает, работает ли timeit с сопрограммами? Вот старинке:
tic = time.time()
t1 = colist2tuple_pool[0].send((t, colist2tuple_pool[1:]))
toc = time.time()
print toc - tic
результат:
0.000446081161499
Новые версии IPython и% TIMIT дают предупреждение:
Самый медленный бег взял 9,04 раза длиннее быстрый. Это может
означает, что промежуточный результат в кэше петли, 1000000 лучше из 3: 317 нс на петле
После некоторого дальнейшего исследования, питон генераторы не магия и отправить еще вызов функции. Причина, по которой мой метод, основанный на генераторе, оказался быстрее, заключается в том, что я делал операцию inplace в списках, что привело к меньшему количеству вызовов функций.
Я написал все это с большим количеством дополнительных деталей за последние talk.
Надеюсь, это поможет кому-то, кто хочет играть с генераторами.
Могу я спросить, почему этот раунд преобразование поездки необходимо? Кроме того, вы пробовали PyPy? –
Вы должны знать, что в Python существует фиксированный предел рекурсии: http://docs.python.org/library/sys.html#sys.setrecursionlimit –
Вы должны делать специализированные функции для каждого преобразования. Это позволит сэкономить дополнительные параметры и тесты –