2

Suppose I have these two classes:

class Branch():
    def __init__(self, id, leaves):
        self.id = id
        self.leaves = leaves
    
class Leaf():
    def __init__(self, color)
        self.color = color
    
    def describe()
        print("This leaf is " + color)

Every Leaf belongs to a Branch. A Branch continues to exist whether it has any leaves or not, but a Leaf cannot meaningfully exist without a Branch (real-life logic aside). That means I can easily get every Leaf from a Branch through the Branch.leaves attribute. If I understood composition correctly, a Branch is the composite and every Leaf in Branch.leaves is one of its components?

However, no Leaf actually knows to which Branch it belongs to. So, I can't have it print "This leaf is orange and belongs to branch #14". I could just add a Leaf.branch attribute and pass it whenever I initialize a new Leaf instance, seeing how a Branch must already exist for that to happen, but that seems very wrong to me, because then it looks like the Branch belongs to the Leaf.

The question is: what is the most pythonic and correct way to do what I described I can't do in my example? Is what I thought felt very wrong not actually bad at all? Or rather, did this problem arise from bad design and I should look at this from another angle? Thanks in advance.

Bernardozomer
  • 57
  • 2
  • 7
  • 1
    "but that seems very wrong to me" it isn't really, although, generally it isn't the greatest design, it is introducing very tight coupling, but it is almost necessary if you require a Leaf to have access to its Branch. And it can be acceptable in a case like this. A canonical data structure, the doubly-linked list, works just like this. So it isn't necessarily "bad". – juanpa.arrivillaga Jan 18 '21 at 18:02
  • 2
    And really, your use-case is a graph/tree, so this isn't unheard of at all. – juanpa.arrivillaga Jan 18 '21 at 18:07
  • 2
    I don't think it's bad to give leaves a branch property if they need to know what branch they belong to. It probably does not belong in the leaf's init, though. Semantically, it seems like it would be better for the branch to add this when a leaf is assigned to the branch. You could do this in the branch's init, or have an `add_leaf()` method on the branch that does this. – Mark Jan 18 '21 at 18:07
  • 1
    I'll add that if a leaf cannot exist without a branch, then perhaps the branch should be responsible for initializing the leaves. – Mark Jan 18 '21 at 18:43
  • Thanks for all the comments, I have a clearer direction in mind now. My actual project is more complex than this, but your solutions remain fit. – Bernardozomer Jan 18 '21 at 18:49

1 Answers1

1

In a complex tree of views and subviews, every individual view has a pointer to the superview and to the window. In a complex tree of menu items and submenus, every item has a pointer to the top menu. It's really common for a child to know its parent or for an item to know its container. Some designs wouldn't be possible without that.

In languages where you have to worry about memory management, the reference to the parent must be a weak reference, which means that the child has never to worry about the live cycle of its parent (while the contrary is not true). Even in Python, the parent owns the child which it is free to create or delete when required but, obviously, the child can never create or delete its parent (which it could not do anyway since it doesn't know its own container or owner).

So, there is a clear hierarchical relationship between the two objects, but that has nothing to do with the fact that they can or cannot know each other.

dspr
  • 2,383
  • 2
  • 15
  • 19