Небезопасно ли получать будущее из contextlib asynccontextmanager?

У меня возникла проблема: мне нужно проверить, что что-то произошло в файле журнала между двумя точками выполнения.

В настоящее время я делаю это:

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.

Я не знаю, правильно ли я понял, вот мое понимание того, что вы пытаетесь: 1. записывает журнал во время работы программы; 2. перед завершением прочитайте журнал и посмотрите, произошло ли что-нибудь примечательное, и хотите вернуть строку при первом появлении, правильно ли я догадался? И какой asyncio.Future будет?

jupiterbjy 05.07.2024 07:17

Я думаю, вы имеете в виду aynsc with obj.bar() as b:. Когда вы проверяете журнал, как вы можете определить, что запись в журнале была сделана между входом и выходом из контекстного менеджера? Также вы можете посмотреть PEP 8 — Руководство по стилю для кода Python.

Booboo 05.07.2024 12:33

@jupiterbjy да, это то, что я собираюсь сделать. У меня есть метод проверки журнала во время выполнения программы, мне просто интересно, есть ли более элегантный способ сделать это с помощью контекстного менеджера или чего-то подобного.

CosmicallyCosmo 05.07.2024 16:47

@Booboo, ах да, моя вина! «Начало» и «Конец» (или аналогичные) записываются в журнал, которые действуют как индикаторы для функции check_log. В настоящее время я делаю это просто до и после интересующего меня кода, меня просто интересовало, можно ли вместо этого использовать диспетчер контекста для управления битами до и после.

CosmicallyCosmo 05.07.2024 16:49

Да, вы можете использовать контекстный менеджер.

Booboo 05.07.2024 17:09

Да, я понимаю, я просто не смог найти пример того, как кто-то дает будущее и устанавливает результат постфактум, и задавался вопросом, известно ли, что это плохая практика.

CosmicallyCosmo 05.07.2024 17:25

Какой смысл создавать b Будущее? Его не ждут, он просто имеет значение. Простая переменная могла бы сделать то же самое.

VPfB 06.07.2024 16:15
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
7
85
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это выглядит совершенно обоснованно и нормально. Фундаментальная вещь, которую следует иметь в виду, это то, что выполнение будет продолжено только в коде, содержащем блок 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, похоже, это именно то, что мне нужно. Спасибо!

CosmicallyCosmo 05.07.2024 18:40

да - несмотря на рекомендацию документации создавать их как объекты верхнего уровня модуля, я часто предпочитаю размещать их как атрибут класса, как в этом примере. Они создаются только один раз, поэтому утечек памяти нет, и они размещаются в пространстве имен, где они будут использоваться.

jsbueno 05.07.2024 19:05

Другие вопросы по теме