【问题标题】:It is possible for python class attribute to make as decorator?python类属性可以作为装饰器吗?
【发布时间】:2018-12-03 00:41:05
【问题描述】:

我正在尝试按照Celery Based Background Tasks 为一个简单的应用程序创建一个 celery 设置。

在我的 task.py

from celery import Celery

def make_celery(app):
    celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'],
                    broker=app.config['CELERY_BROKER_URL'])
    celery.conf.update(app.config)
    TaskBase = celery.Task
    class ContextTask(TaskBase):
        abstract = True
        def __call__(self, *args, **kwargs):
            with app.app_context():
                return TaskBase.__call__(self, *args, **kwargs)
    celery.Task = ContextTask
    return celery

此方法适用于主flask应用程序的app.py

from flask import Flask

flask_app = Flask(__name__)
flask_app.config.update(
    CELERY_BROKER_URL='redis://localhost:6379',
    CELERY_RESULT_BACKEND='redis://localhost:6379'
)
celery = make_celery(flask_app)


@celery.task()
def add_together(a, b):
    return a + b

我的用例是我想在其中创建另一个模块 helpers.py 可以定义异步类的集合。分开 基于 celery 的方法并使其模块化。

我所做的是将task.py 模块调用到其他模块helpers.py 以创建一个类AsyncMail 来处理电子邮件操作后台工作。

from task import make_celery

class AsyncMail(object):

    def __init__(self, app):
        """
            :param app: An instance of a flask application.  
        """   

        self.celery = make_celery(app)

    def send(self, msg):
        print(msg)

现在我如何访问self.celery 属性来作为类的任何方法的装饰器?

@celery.task()
def send(self, msg):
    print(msg)

如果不可能,还有什么其他替代步骤可以解决这个问题?

【问题讨论】:

    标签: python flask task celery python-decorators


    【解决方案1】:

    你不能做你想做的事。在定义类的时候,没有self,更不用说self.celery,所以你不能使用@self.celery。即使你有某种时间机器,也可能会创建 38 个不同的 AsyncMail 实例,你想要哪个 self.celery 在这里?


    在了解如何做你想做的事之前,你确定你想要吗?你真的希望每个AsyncMail 对象都有自己独立的 Celery 吗?通常每个应用程序只有一个,这就是为什么通常不会出现这种情况的原因。


    如果你真的想要,你可以在你有一个对象来装饰它们之后给每个实例装饰方法。但它会很丑。

    def __init__(self, app):
        self.celery = make_celery(app)
    
        # We need to get the function off the class, not the bound method off self
        send = type(self).send
    
        # Then we decorate it manually—this is all @self.celery.task does
        send = self.celery.task(send)
    
        # Then we manually bind it as a method
        send = send.__get__(self)
    
        # And now we can store it as an instance attribute, shadowing the class's
        self.send = send
    

    或者,如果您更喜欢将所有内容放在一行中:

        self.send = self.celery.task(type(self).send).__get__(self)
    

    对于 Python 2,“类外函数”实际上是一个未绑定的方法,IIRC 你必须在最后调用__get__(self, type(self)) 将其转换为绑定方法,否则应该都是一样的。

    【讨论】:

    • 这里的def send(self, msg): 被删除了吗?
    • @Shift'NTab 不,你必须在 somewhere 中定义它,并且该类与任何地方一样好。尽管如果您想在 __init__ 或全局函数或其他任何地方在本地定义它,但只要稍作更改,这也可以工作(例如,如果它是在本地定义的,您显然不需要 send = type(self).send而不是在课堂上)。
    • 回答您的问题。不,我不想要。我意识到,在创建AsyncMail 的实例时,每次调用它都会创建另一个 celery 实例。现在知道这种方式似乎我需要重组类,而不是将烧瓶应用程序实例作为参数,我可以使用 app.py 中定义的 celery 实例来避免多个 celery 成员。
    • 如果你坚持使用这种方法,似乎代码可以工作,但现在这是一个真正的问题,它会在未来导致更复杂的错误。你的回答可以说明一些问题。感谢您的见解
    猜你喜欢
    • 1970-01-01
    • 2016-07-30
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 2019-02-02
    • 2021-05-17
    • 2020-07-09
    • 2020-10-07
    相关资源
    最近更新 更多