How to communicate between sibling objects in Python, when using Composition, not Inheritance

Viewed 408

I have a parent object which is composed of 2 (different) child objects. The 2 child instances need to communicate. For example, assume child1 needs to send something to child2:

import children

class Parent:
    def __init__(self):
        self.child1 = children.Child1(self.q)
        self.child2 = children.Child2(self.q)

parent = Parent()
parent.child1.send("string")

Is there a recommended pattern to achieve this?


The best I could come up with was to create a Queue between the 2 objects. This works, but it requires the receiving object to run a thread. For example:

parent.py:

import children
import queue
import time

class Parent:
    def __init__(self):
        
        self.q = queue.Queue()
        self.child1 = children.Child1(self.q)
        self.child2 = children.Child2(self.q)

parent = Parent()
parent.child1.send("string")
time.sleep(1)

children.py:

import threading

class Child1:

    def __init__(self, q):
        self.q = q

    def send(self, item):
        self.q.put(item)

class Child2:

    def __init__(self, q):
        self.q = q
        self.receiver = threading.Thread(target=self.worker, daemon=True).start()

    def worker(self):
        """Process the queue"""
        while True:
            item = self.q.get()
            print(f"{item} received")

In practice, the "items" I send in the queue are a function name and an argument list. This is basically the Command pattern as described here. But I don't like the need for receiver threads.

I would prefer if it was possible to allow one object to directly call a method in the other object. If there was an inheritance relationship between them, with a common parent, I could maybe use super() for this:

class Child1:

    def send(self, function, arguments):
        super().child2.function(arguments)

But there is no inheritance in my case: just composition. Is there a better way?

1 Answers

Just construct the children with a reference back to the parent:

class Child1:
    def __init__(self, parent):
        self.parent = parent

    def send(self, msg):
        self.parent.child2.print_out(msg)

class Child2:
    def __init__(self, parent):
        self.parent = parent

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

class Parent:
    def __init__(self):
        self.child1 = Child1(self)
        self.child2 = Child2(self)

parent = Parent()
parent.child1.send("foo")
Related