python class属性是否可以用作装饰器?

时间:2018-06-24 02:31:42

标签: python flask task celery python-decorators

我正在尝试遵循此Celery Based Background Tasks来为一个简单的应用程序创建一个芹菜设置。

在我的 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

此方法在主烧瓶应用程序的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   可以定义异步类的集合。分开   基于芹菜的方法,并将其模块化。

我所做的是将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)

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

1 个答案:

答案 0 :(得分:2)

您无法做您想做的事情。在定义类时,没有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))才能将其最终转换为绑定方法,但是否则,它们应该都相同