Можно ли сделать атрибут класса Python в качестве декоратора? - PullRequest
0 голосов
/ 24 июня 2018

Я пытаюсь следовать этому Фоновые задачи на основе сельдерея , чтобы создать настройки сельдерея для простого приложения.

В моем 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 голосов
/ 24 июня 2018

Ты не можешь делать то, что пытаешься сделать.Во время определения класса, self, гораздо меньше self.celery, для вызова нет, поэтому вы не можете использовать @self.celery.Даже если бы у вас была какая-то машина времени, может быть создано 38 различных AsyncMail экземпляров, и какой из них self.celery вы бы хотели здесь?хочешь, ты уверен, что хочешь ?Вы действительно хотите, чтобы у каждого объекта AsyncMail был свой отдельный сельдерей?Обычно у вас есть только один на приложение, поэтому обычно это не подходит.


Если вы действительно хотите, вы можете дать каждому экземпляру оформленные методы после того, как у вас есть объект для их украшения,Но это будет ужасно.

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 функция "off" отключенакласс "на самом деле является несвязанным методом, и IIRC вам нужно вызвать __get__(self, type(self)), чтобы в конце превратить его в связанный метод, но в остальном все должно быть одинаково.

...