Método de la clase Python encadenamiento

Para evitar perderse en las decisiones arquitectónicas, pediré esto con un ejemplo análogo:

Digamos que quería un patrón de clase Python como este:

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

La pregunta aquí es cómo puedo obtener un diseño de una clase tal que ciertos métodos puedan ser “encadenados” de esta manera.

task() requeriría acceso a los atributos de instancia de clase de queue y las operaciones de task dependen de la salida de region() .

Veo que SQLalchemy hace esto (ver más abajo) pero tengo dificultades para revisar su código y aislar este patrón.

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

SQLAlchemy produce un clon en tales llamadas, vea el método Generative._generate() que simplemente devuelve un clon del objeto actual.

En cada llamada de método generativo (como .filter() , .orderby() , etc.) se devuelve un nuevo clon, con un aspecto específico alterado (como el árbol de consultas expandido, etc.).

SQLAlchemy utiliza un decorador @_generative para marcar los métodos que deben operar y devolver un clon aquí, intercambiándose self por el clon producido.

Usar este patrón en tu propio código es bastante simple:

 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)) 

Cada llamada a .region() o .task() producirá ahora un clon, que el método decorado actualiza al alterar el estado. A continuación, se devuelve el clon, sin modificar el objeto de instancia original.

Solo devuelve el objeto actual del método de region , como este

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

Como la region devuelve el objeto actual que tiene la función de task , el encadenamiento es posible ahora.

Nota:

Como @chepner mencionó en la sección de comentarios , asegúrese de que la region realice cambios en el objeto.