Цепь метода класса Python

Чтобы не потеряться в архитектурных решениях, я попрошу об этом с помощью аналогичного примера:

скажет, что мне нужен шаблон класса Python следующим образом:

queue = TaskQueue(broker_conn)
queue.region("DFW").task(fn, "some arg") 

Вопрос здесь в том, как мне получить дизайн класса, так что некоторые методы могут быть "закованы в цепочку" таким образом.

task() потребует доступа к атрибутам экземпляра класса queue, а операции task зависят от вывода region().

Я вижу, что SQLalchemy делает это (см. ниже), но мне трудно перекопать их код и выделить этот шаблон.

query = db.query(Task).filter(Task.objectid==10100) 

Ответ 1

SQLAlchemy создает клон для таких вызовов, см. Generative._generate() method, который просто возвращает клон текущего объекта.

В каждом вызове генерирующего метода (например, .filter(), .orderby() и т.д.) возвращается новый клон с измененным конкретным аспектом (например, дерево запросов и т.д.).

SQLAlchemy использует @_generative decorator, чтобы отметить методы, которые должны работать и возвращать клон здесь, заменяя self для созданного клона.

Использование этого шаблона в вашем собственном коде довольно просто:

from functools import wraps

class GenerativeBase(object):
    def _generate(self):
        s = self.__class__.__new__(self.__class__)
        s.__dict__ = self.__dict__.copy()
        return s

def _generative(func):
    @wraps(func)
    def decorator(self, *args, **kw):
        new_self = self._generate()
        func(new_self, *args, **kw)
        return new_self
    return decorator


class TaskQueue(GenerativeBase):
    @_generative
    def region(self, reg_id):
        self.reg_id = reg_id

    @_generative
    def task(self, callable, *args, **kw):
        self.tasks.append((callable, args, kw))

Каждый вызов .region() или .task() теперь будет производить клон, который украшенный метод обновляет, изменяя состояние. Затем возвращается клон, оставляя исходный объект экземпляра неизменным.

Ответ 2

Просто верните текущий объект из метода region, как этот

def region(self, my_string):
    ...
    ...
    return self

Так как region возвращает текущий объект с функцией task, цепочка возможна теперь.

Примечание:

Как @chepner упомянутый в разделе комментариев, убедитесь, что region вносит изменения в объект self.