У меня возникла проблема: мне нужно проверить, что что-то произошло в файле журнала между двумя точками выполнения.
В настоящее время я делаю это:
print("start")
# do something here
print("end")
res = await check_log(...) # check in the log if something's happened
# between start and end and return the line if so
Мне интересно, могу ли я вместо этого использовать asynccontextmanager contextlib, который выглядит так:
class foo():
def __init__(self, ...):
...
@asynccontextmanager
async def bar(self):
print("start")
fut = asyncio.Future()
yield fut
print("end")
res = await check_log(...)
fut.set_result(res)
и который я называю следующим образом, где класс содержит переменные, которые в противном случае мне пришлось бы передать в check_log:
obj = foo(...)
async with obj.bar() as b:
# do something here
res = b.result()
Есть ли в этом что-то принципиально небезопасное или неправильное? Если да, то есть ли лучший способ сделать это? Я знаю, что с помощью обычного менеджера контекста вы можете обойти это, установив атрибут, хотя я не уверен, что это возможно с помощью contextlib.
Я думаю, вы имеете в виду aynsc with obj.bar() as b:. Когда вы проверяете журнал, как вы можете определить, что запись в журнале была сделана между входом и выходом из контекстного менеджера? Также вы можете посмотреть PEP 8 — Руководство по стилю для кода Python.
@jupiterbjy да, это то, что я собираюсь сделать. У меня есть метод проверки журнала во время выполнения программы, мне просто интересно, есть ли более элегантный способ сделать это с помощью контекстного менеджера или чего-то подобного.
@Booboo, ах да, моя вина! «Начало» и «Конец» (или аналогичные) записываются в журнал, которые действуют как индикаторы для функции check_log. В настоящее время я делаю это просто до и после интересующего меня кода, меня просто интересовало, можно ли вместо этого использовать диспетчер контекста для управления битами до и после.
Да, вы можете использовать контекстный менеджер.
Да, я понимаю, я просто не смог найти пример того, как кто-то дает будущее и устанавливает результат постфактум, и задавался вопросом, известно ли, что это плохая практика.
Какой смысл создавать b Будущее? Его не ждут, он просто имеет значение. Простая переменная могла бы сделать то же самое.






Это выглядит совершенно обоснованно и нормально.
Фундаментальная вещь, которую следует иметь в виду, это то, что выполнение будет продолжено только в коде, содержащем блок with, после выполнения метода __aexit__ в контекстном менеджере, что означает выполнение части после yield fut в вашем методе и, следовательно, завершение await check_log(...) исполнение.
Если вы хотите, чтобы check_log выполнялся одновременно с блоком, следующим за блоком with, это так же легко сделать, изменив создание check_log как задачу и установив для него done callback функцию, которая будет устанавливать результат fut - тогда в этом примере , b можно ожидать всякий раз, когда кто-то захочет проверить этот результат. В противном случае, это хорошо, как есть. (Просто, пожалуйста, поставьте это утверждение yield и блок после него в составное try-finally).
Конечно, обратите внимание: если кто-то попытается await b внутри блока with, ваш код заблокируется.
Если у вас есть какое-то значение, которое код post- with может получить без побочных эффектов, вы можете использовать contextvar.ContextVars в качестве атрибута класса foo:
import contextvars
class foo():
last_log = contextvars.ContextVar("last_log")
def __init__(self, ...):
...
@asynccontextmanager
async def bar(self):
print("start")
try:
yield None
finally:
print("end")
self.last_log.set(await check_log(...))
...
obj = foo(...)
async with obj.bar():
# do something here
res = obj.last_log.get()
Ах, я раньше не сталкивался с contextVar, похоже, это именно то, что мне нужно. Спасибо!
да - несмотря на рекомендацию документации создавать их как объекты верхнего уровня модуля, я часто предпочитаю размещать их как атрибут класса, как в этом примере. Они создаются только один раз, поэтому утечек памяти нет, и они размещаются в пространстве имен, где они будут использоваться.
Я не знаю, правильно ли я понял, вот мое понимание того, что вы пытаетесь: 1. записывает журнал во время работы программы; 2. перед завершением прочитайте журнал и посмотрите, произошло ли что-нибудь примечательное, и хотите вернуть строку при первом появлении, правильно ли я догадался? И какой
asyncio.Futureбудет?