В диспетчере контекста Python наблюдается жуткое действие - PullRequest
3 голосов
/ 18 февраля 2020

Использование contextmanager в Python 3 (я не проверял Python 2) имеет странное поведение в отношении переменных, объявленных в области действия в предложении with.

Мне кажется, что переменные ведут себя как «пугающее действие на расстоянии» в том смысле, что только при наблюдении они кажутся существующими ( шутка от не-физически подкованного инженера ).

Внутри контекста менеджеров контекста после предела доходности:

Если вы ...

  1. распечатаете locals()

, тогда переменная не Существуют.

Но если вы:

  1. распечатываете locals();
  2. делаете что-либо с переменной управляемой области действия

ТОГДА ПЕРЕМЕННОЕ СУЩЕСТВУЕТ !!

См. Этот пример:

from contextlib import contextmanager

def groucho():
    @contextmanager
    def groucho_manager(**kwargs):
        yield
        print("groucho_manager locals", locals())
        a

    with groucho_manager(lolcat=10):
        a = 50

def harpo():
    @contextmanager
    def harpo_manager(**kwargs):
        yield
        print("harpo_manager locals", locals())

    with harpo_manager(lolcat=10):
        b = 100

groucho()
harpo()

выходы:

groucho_manager locals {'kwargs': {'lolcat': 10}, 'a': 50}
harpo_manager locals {'kwargs': {'lolcat': 10}}

Возможно, связано с Python наследование класса - жуткое действие , но я не уверен.

1 Ответ

0 голосов
/ 18 февраля 2020

Мой первоначальный вариант использования - сделать inner scope aware context manager для ведения журналов.

Я думал, что могу злоупотребить странным поведением, перечисленным в вопросе, но, как указал @jasonharper: это связано с вложенным Области и не злоупотреблять.

Итак ... я решил сделать действительно хакерское / оскорбительное решение для всего этого:

scope_injected_contextmanager на pypi

вот простой пример из README:

from scope_injected_contextmanager import scope_injected_contextmanager

fetch = lambda request: ("ok", 200)

@scope_injected_contextmanager
def log_request(request, response):
    print(f"request: {request} response: {response}")

with log_request:
    request = {
        "query_args": {
            'foo': 10
        }
    }
    response = fetch(request)

# prints
# request: {'query_args': {'foo': 10}} response: ('ok', 200)

подробнее на github

будьте осторожны ... его:

  1. действительно хакерский
  2. совсем не проверен - недостаточно
  3. действительно уродлив и оскорбителен
  4. использует inspect тяжело (см. 1-3)
...