Установка атрибута в вызываемом диспетчере контекста из декорированной функции - PullRequest
0 голосов
/ 07 декабря 2018

Я играю с менеджерами контекста и декораторами в Python и создал вызываемый класс декоратора менеджера контекста.У меня это украшает функцию, где я хочу изменить атрибут в классе декоратора.Вот простая версия класса декоратора:

class CallableDecorator:
    def __init__(self):
        print('Creating decorator')
        self.foo = None
    def __enter__(self):
        print('Entering Decorator')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f'Exiting Decorator with attribute foo = {self.foo}')
    def __call__(self, func):
        print('Starting the call in Decorator')
        @wraps(func)
        def wrapper(*args, **kwargs):
            with self:
                print('In wrapped context manager')
                return func(*args, **kwargs)
        print('About to finish call in Decorator')
        return wrapper

Затем я обертываю функцию, подобную

@CallableDecorator()
def bar():
    something = do_stuff()
    setattr(callable_decorator, 'foo', something)
    print('bar')

, и она сразу напечатает

Creating decorator
Starting the call in Decorator
About to finish call in Decorator

, потому чтоэто в значительной степени вызывает CallableDecorator()bar(), поэтому при создании этой функции создается объект типа CallableDecorator.После вызова bar() это печатается:

Entering Decorator
In wrapped context manager
bar
Exiting Decorator with foo = None

Что также ожидается, потому что теперь я звоню wrapper.Однако я хочу изменить атрибут foo в CallableDecorator с bar на значение, которое вычисляется в функции bar, но неизвестно на момент определения bar.Есть ли какой-нибудь доступ к этому?

Я не спрашиваю, хороший ли это дизайн или когда это когда-нибудь пригодится, я просто пытаюсь понять, как это сделать.

1 Ответ

0 голосов
/ 07 декабря 2018

Вы можете сделать так, чтобы обертка передавала сам объект декоратора в качестве параметра при вызове func:

class CallableDecorator:
    def __call__(self, func):
        print('Starting the call in Decorator')
        @wraps(func)
        def wrapper(*args, **kwargs):
            with self:
                print('In wrapped context manager')
                return func(self, *args, **kwargs)
        print('About to finish call in Decorator')
        return wrapper

, чтобы func мог принять объект декоратора в качестве параметра и установить его foo атрибут внутри функции:

@CallableDecorator()
def bar(decorator):
    decorator.foo = 'bar'

С этими изменениями ваш код выведет:

Creating decorator
Starting the call in Decorator
About to finish call in Decorator
Entering Decorator
In wrapped context manager
Exiting Decorator with attribute foo = bar
...