【问题标题】:Setting attribute in callable context manager decorator from decorated function从装饰函数设置可调用上下文管理器装饰器中的属性
【发布时间】:2019-05-09 14:18:23
【问题描述】:

我正在使用 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。但是,我想将CallableDecorator 中的foo 属性从bar 更改为在bar 函数中计算的值,但在定义bar 时未知。有没有办法访问这个?

我不是在问这是否是一个好的设计,或者何时有用,我只是想了解如何做到这一点。

【问题讨论】:

    标签: python python-decorators callable contextmanager


    【解决方案1】:

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

    【讨论】:

    • 对不起,我应该更明确一点。我想要更新的值在定义 bar 时是未知的。它将在 bar 内部创建。
    猜你喜欢
    • 2013-03-25
    • 2021-08-24
    • 2017-01-06
    • 2022-10-14
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 2012-03-02
    • 1970-01-01
    相关资源
    最近更新 更多