Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python parent child relationship class

Tags:

python

I've written up a class as seen below. I want to add the attribute of 'parent' to my base class Node. I was wondering if someone could show me how to properly do this. I've been given guidance on how to do it but I'm not entire sure how to write it syntax wise. Here is the suggested way to do it...

generally I would hide the parent attribute behind a property so when its set, the children array of the previous parent can be modified so if you say n.parent = x, it actually remove node n from it's parent and set the parent value

class Node(object):
    def __init__(self, name, attributes, children):
        self.name = name
        self.attributes = attributes if attributes is not None else {}
        self.children = children if children is not None else []


class Camera(Node):
    def __init__(self, name="", attributes=None, children=None, enabled=True):
        super(Camera, self).__init__(name=name, attributes=attributes, children=children)
        self.enabled = enabled

updated

import weakref

class Node(object):
    _parent = None

    def __init__(self, name, attributes, children, parent):
        self.name = name
        self.attributes = attributes if attributes is not None else {}
        self.children = children if children is not None else []
        self.parent = parent

        for child in children:
            child.parent = self

    @property
    def parent(self):
        return self._parent() if self._parent is not None else None

    @parent.setter
    def parent(self, newparent):
        oldparent = self.parent

        if newparent is oldparent:
            return
        if oldparent is not None:
            oldparent.children.remove(self)
        if self not in newparent.children:
            newparent.children.append(self)
        self._parent = weakref.ref(newparent) if newparent is not None else None


class Camera(Node):
    def __init__(self, name="", attributes=None, children=None, enabled=True, parent=None):
        super(Camera, self).__init__(name=name, attributes=attributes, children=children, parent=parent)
        self.enabled = enabled




Camera()
like image 593
JokerMartini Avatar asked Feb 02 '26 12:02

JokerMartini


1 Answers

Example code, incorporating weakref to avoid reference cycles that can delay cleanup (or prevent it entirely in some cases, particularly on Python 3.3 and earlier):

import weakref

class Node:
# If this is Python 2, you need to explicitly inherit from object to 
# be a new-style class with descriptor support (which allows properties), so
# the class line would be:
# class Node(object):
# On Py3, it's implicit and can be omitted

    # Ensure attribute readable so getter/setter don't need to use has/getattr
    # Will be allocated per-instance when self.parent is assigned in __init__
    # So on Py3.3+, it will still get the compact key-sharing dicts for attributes
    _parent = None

    # Adding defaults for all values matching Camera for simplicity
    def __init__(self, name='', attributes=None, children=None, parent=None):
        self.name = name
        self.attributes = attributes if attributes is not None else {}
        self.children = children if children is not None else []
        self.parent = parent
        for child in children:
            child.parent = self

    @property
    def parent(self):
        return self._parent() if self._parent is not None else None

    @parent.setter
    def parent(self, newparent):
        oldparent = self.parent
        # If setting to existing parent, then no-op
        # Remove this check and early-out if you want to be able to move
        # a node to the end of its parent's children by reassigning the same parent
        if newparent is oldparent:
            return
        if oldparent is not None:
            oldparent.children.remove(self)
        if self not in newparent.children:
            newparent.children.append(self)
        self._parent = weakref.ref(newparent) if newparent is not None else None

Typically, to avoid issues with changing parent class prototypes, I put additional parameters to child class __init__ methods first, not last. Because I gave __init__ defaults on Camera, this makes Camera very simple:

class Camera(Node):
    def __init__(self, enabled=True, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # On Py2, super isn't magic, so you need to be explicit unlike Py3:
        # super(Camera, self).__init__(*args, **kwargs)
        self.enabled = enabled

As you can see, by moving the Camera unique __init__ param to the front, Camera can stop paying attention to changes in the Node __init__; the new Camera works with the original Node or the new Node (that accepts parent and assigns self.parent) just fine, because it's less tightly coupled to the exact parameter ordering. Note that this does mean that if enabled is not passed positionally, then all arguments must be passed by keyword.

Please comment if I made any mistakes, but that should be close to correct. In general, I had the Node class use the parent accessor to simplify the code by removing the difficulty with handling None properly (None is not weak referencable).

like image 139
ShadowRanger Avatar answered Feb 04 '26 01:02

ShadowRanger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!