通过装饰函数在可调用上下文管理器装饰器中设置属性

时间:2018-12-07 19:42:56

标签: python python-decorators callable contextmanager

我正在使用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。但是,我想将fooCallableDecorator中的bar属性更改为在bar函数中计算出的值,但在定义时尚不知道bar。反正有访问权限吗?

我不是问这是一个好的设计,还是什么时候有用,我只是想了解如何做到这一点。

1 个答案:

答案 0 :(得分:0)

您可以使包装器在调用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