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

Question:

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?

Asked By: banjaxed

||

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")
Answered By: Aplet123

I believe what you’re looking for is the Façade Pattern.

Presumably, Parent is more than just a namespace; it knows things and does stuff. Having Child1 send something to Child2 seems like it would be a behaviour of Parent, the implementation of which is abstracted away, hidden behind the façade.

class Foo:

    def send_msg(self):
        return f'This is a message sent from {self.__class__.__name__}.'


class Bar:

    def receive_msg(self, msg):
        print(self.__class__.__name__, 'has received the following message:'
        print('t', msg)


class Facade:

    def __init__(self):
        self.foo = Foo()
        self.bar = Bar()

    def foo_the_bar(self):
        self.bar.receive_msg(self.foo.send_msg())
>>> facade = Facade()
>>> facade.foo_the_bar()
Bar has received the following message:
     This is a message sent from Foo.
Answered By: ibonyun
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.