Представьте, что у вас есть функция, которая обрабатывает тяжелое вычислительное задание, которое мы хотим выполнить асинхронно в контексте приложения Tornado. Более того, мы хотели бы лениво оценить функцию, сохранив ее результаты на диске и не повторять функцию дважды для тех же аргументов.Комбинируйте торнадо gen.coroutine и worklib mem.cache decorators
без кэширования результата (запоминание) один будет сделать следующее:
def complex_computation(arguments):
...
return result
@gen.coroutine
def complex_computation_caller(arguments):
...
result = complex_computation(arguments)
raise gen.Return(result)
Предположит, для достижения функции запоминания, мы выбираем памяти класса от joblib. Просто декорирования функцию с @mem.cache
функция может быть легко memoized:
@mem.cache
def complex_computation(arguments):
...
return result
где mem
может быть что-то вроде mem = Memory(cachedir=get_cache_dir())
.
Теперь рассмотрим объединение двух, где мы выполняем сложную вычислительную функцию исполнителя:
class TaskRunner(object):
def __init__(self, loop=None, number_of_workers=1):
self.executor = futures.ThreadPoolExecutor(number_of_workers)
self.loop = loop or IOLoop.instance()
@run_on_executor
def run(self, func, *args, **kwargs):
return func(*args, **kwargs)
mem = Memory(cachedir=get_cache_dir())
_runner = TaskRunner(1)
@mem.cache
def complex_computation(arguments):
...
return result
@gen.coroutine
def complex_computation_caller(arguments):
result = yield _runner.run(complex_computation, arguments)
...
raise gen.Return(result)
Поэтому первый вопрос, является ли вышеупомянутый подход технически правильно?
Теперь давайте рассмотрим следующий сценарий:
@gen.coroutine
def first_coroutine(arguments):
...
result = yield second_coroutine(arguments)
raise gen.Return(result)
@gen.coroutine
def second_coroutine(arguments):
...
result = yield third_coroutine(arguments)
raise gen.Return(result)
Второй вопрос: как можно memoize second_coroutine
? Является ли это правильно сделать что-то вроде:
@gen.coroutine
def first_coroutine(arguments):
...
mem = Memory(cachedir=get_cache_dir())
mem_second_coroutine = mem(second_coroutine)
result = yield mem_second_coroutine(arguments)
raise gen.Return(result)
@gen.coroutine
def second_coroutine(arguments):
...
result = yield third_coroutine(arguments)
raise gen.Return(result)
[UPDATE I]Caching and reusing a function result in Tornado обсуждает с использованием functools.lru_cache
или repoze.lru.lru_cache
в качестве решения для второго вопроса.
Спасибо, Бен. Да. Ты прав. Я уже тестировал 'functools.lru_cache' и работает как шарм. Что касается вашего примера, я полагаю, что «кеш» - это что-то, предоставляемое внешним контекстом, поскольку вы, кажется, не определяете что-либо внутри закрытия внешнего декоратора. Тем не менее, ваш пример для меня совершенно ясен, чтобы понять эту идею. Ура! –