python parent child relationship class - 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()

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

Related

Finding object references in Python

I have written following method to get a parent container of an object. The object may be referenced by multiple containers, so I also provide container class names to that method in the order correct with nesting order.
def get_parent(element, *argv):
def find_object_in_referrer(element, parent_class):
gc.collect()
for referrer in gc.get_referrers(element):
if parent_class.__name__ not in referrer:
continue
for key, object in referrer.items():
if not isinstance(object, parent_class):
continue
return object
return None
return None
for parent_class in argv:
object = find_object_in_referrer(element, parent_class)
if not object:
None
element = object
return element
It works as follows:
class Parent(object):
def __init__(self, child):
self.child = child
class Parent2(object):
def __init__(self, child):
self.child = child
class Parent3(object):
def __init__(self, child):
self.child = child
child = Child()
parent = Parent(child)
parent2 = Parent2(parent)
parent3 = Parent3(parent2)
get_parent(child, Parent, Parent2, Parent3)
The last line, get_parent() call, should return reference to parent3 object.
That code sometimes works while debugging but never works in a normal execution.
Why get_referrers() function behaves different while in debugging mode?
I know this is not recommended to use that function. Is there any other method to achieve the same - implement a function to get a parent container?

Inner class overwriting class property

I have a Python script containing the following code:
class Parent:
def __init__(self, name):
self.name = name
class Child:
def __init__(self, name):
self.name = name
def print_relationship(self):
print(f'{self.name} is {self.Child.name}\'s parent')
myObj_1 = Parent('John')
myObj_1.Child.name = 'Steve'
myObj_2 = Parent('Stan')
myObj_2.Child.name = 'Oliver'
myObj_1.print_relationship()
myObj_2.print_relationship()
which returns:
>>> John is Oliver's parent
>>> Stan is Oliver's parent
but, I'm expecting the following results:
>>> John is Steve's parent
>>> Stan is Oliver's parent
Is this an inheritance issue? Bad design? Both?
-edit- Bad design was the answer.
The class Child represents children in general. Define it globally. Each instance of Parent should store a reference to an instance of Child. (That instance may not be unique; a child could have multiple parents.)
class Child:
def __init__(self, name):
self.name = name
class Parent:
def __init__(self, name, child=None):
self.name = name
self.child = child
def print_relationship(self):
print(f'{self.name} is {self.child.name}\'s parent')
oliver = Child('Oliver')
myObj_1 = Parent('John', Child('Steve'))
myObj_2 = Parent('Stan', oliver)
myObj_3 = Parent('Jane', oliver)
myObj_1.print_relationship()
myObj_2.print_relationship()
Of course, a parent could have multiple children:
class Parent:
def __init__(self, name, children=None):
self.name = name
if children is None:
children = []
self.children = children
def print_relationship(self):
for child in self.children:
print(f'{self.name} is {child.name}\'s parent')
myObj1 = Parent('Bob', [Child('Alice'), Child('Chloe')])
In the linemyObj_1.Child.name = 'Steve', you are creating a class attribute called name on Child class and assigning value of Steve. This name is different from the instance attribute in the line self.name = name which can only be accessed by instance of the class Child.
When myObj_2.Child.name = 'Oliver' gets executed, the value of class attribute name of Child class is changed.
In print_relationship, you are referring to self.Child.name, which refers to the class attribute name of Child class.

Python - inner class is not defined?

I have to do an unrolled linked list for one of my classes. I'm new to python, but not to programming, and for some reason I cannot get around this little problem!
I have a class Node that is to be the node object used within the unrolled linked list. The unrolled linked list class performs all the operations on the Node class.
class UnrolledLinkedList(object):
""" INNER NODE CLASS """
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
""" END NODE CLASS """
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = Node()
""" OTHER FUNCTIONS OF UNROLLEDLINKEDLIST CLASS """
The problem comes at the last line of the UnrolledLinkedList class' init function: "global name Node is not defined". I double checked my indentation and looked all over the internet for examples of something like this, but couldn't find any. Would someone mind explaining to me what's wrong?
Methods do not include their class as a scope to be searched. If you want this to work then you will need to use either UnrolledLinkedList.Node or self.Node instead.
The inner class Node is a member of the class UnrolledLinkedList and can only be accessed via self.
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = self.Node()
Use:
self.head = self.Node()
and it works.
A class does not create its own name space. Using self.Node(), Python first searches all attributes of the instances. Since it does not find the name Node there, it it searches the class UnrolledLinkedList for Node.
Alternatively, you can use the class name directly:
UnrolledLinkedList.Node()
You can achieve the same without nesting the class Node:
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
class UnrolledLinkedList(object):
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = Node()
Qualify Node() with self:
class UnrolledLinkedList(object):
class Node(object):
def __init__(self):
self.array = []
self.next_node = None
def __init__(self, max_node_capacity=16):
self.max_node_capacity = max_node_capacity
self.head = self.Node()
Python needs to qualify references to things. In this case, you could either say UnrolledLinkedList.Node() or self.Node().

python: set read-only attribute for class objects

I've created a class object called 'Node'. I then created two subclasses 'Beetle' and 'Dakota'. You'll notice there is an attr called 'superclass' which is part of the baseclass. I want this attribute to be set for each subclass and once it's set it should never be changed. It's a read-only attribute. I was wondering how to set this attribute up correctly in order to be a read only attribute?
class Node(object):
def __init__(self, name, superclass, attributes, children):
self.name = name
self.superclass = superclass
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
class Beetle(Node):
def __init__(self, name="", superclass="Cars", attributes=None, children=None, enabled=True):
super(Beetle, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
class Dakota(Node):
def __init__(self, name="", superclass="Trucks", attributes=None, children=None, enabled=True):
super(Dakota, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
Rename you variable to add __ to the begining
self.__superclass = superclass
you can't access self.__superclass with something like Dakota().__superclass
if you want to get value of __superclass add a function in Node class to return it.
def getsuperclass(self):
return self.__superclass
...
Dakota().getsuperclass()
To have a 'read-only' property in a class you can make use of the #property decoration.
An example:
class Dakota(object):
def __init__(self, superclass):
self.__superclass = superclass
#property
def superclass(self):
return self.__superclass
Usage:
>>> a = Dakota('lol')
>>> a.superclass
'lol'
>>> a.superclass = 'hah'
AttributeError...
AttributeError: can't set attribute
You can refer to this other answer thread

Class variable in python context class

I am writing a series of nested contexts, and need to keep trace of their relative relationship. But I ran in the following behavior that I did not expect:
class NestedContext():
_current = None
def __enter__(self):
self.parent = self._current
self._current = self
def __exit__(self, _type, _value, _tb):
self._current = self.parent
with NestedContext():
assert NestedContext._current is not None # fails
with NestedContext():
pass
The assert fail because the class variable _current is None, which is unexpected, since I thought __enter__ just set it to self. This seems to be related some deeper behavior of Python context that I don't understand. Can anyone explain? Thanks.
You are testing the class attribute. You set self._current, an instance attribute.
If you wanted to set the class attribute, you need to qualify it as such:
class NestedContext(object):
_current = None
def __enter__(self):
self.parent = self._current
NestedContext._current = self
def __exit__(self, _type, _value, _tb):
NestedContext._current = self.parent
When reading an attribute, Python searches through the layers of instance, class, and base classes. But when setting an attribute, no such layering takes place. So self._current will fall back to NestedContext if there is no such attribute on self, but self._current = some_value sets that attribute on self, not on the class.
I also used object as the parent class for NestedContext; unless there are compelling reasons not to (e.g. you are working with legacy code that relies on the old behaviours), you always want to get new style classes.
Demo (with addition of a _name attribute and a __repr__ method):
>>> class NestedContext(object):
... _current = None
... def __init__(self, name):
... self._name = name
... def __repr__(self):
... return '<NestedContext({._name!r})>'.format(self)
... def __enter__(self):
... self.parent = self._current
... NestedContext._current = self
... def __exit__(self, _type, _value, _tb):
... NestedContext._current = self.parent
...
>>> with NestedContext('outer'):
... print NestedContext._current
... with NestedContext('inner'):
... print NestedContext._current
... print NestedContext._current.parent
...
<NestedContext('outer')>
<NestedContext('inner')>
<NestedContext('outer')>

Categories

Resources