How do I get a parent node through recursion in python? - python-3.x

In python, using constructors and Class I have created nodes. I need to get its parent and their parent nodes. I have got that node, its parent(that has marked when created by a constructor).
I also tried to create a function to print a node and its parents. However how do I pass the parents'parent node so i can get that also.

Given that you didnt show any code its hard to tell what your really trying to do. However assuming your mean nodes like in a tree then below simple example shows how you could recursivly work back up the tree to show the relationships. this of course is not a complete example but should give you an idea.
class node():
def __init__(self, name, parent=None):
self.name = name
self.parent: node = parent
def get_bottom_up_ancestors(self):
if self.parent:
return [self.name] + self.parent.get_bottom_up_ancestors()
return [self.name]
def get_top_down_ancestors(self):
return self.get_bottom_up_ancestors()[::-1]
root = node("Top")
child1 = node("first", parent=root)
child2 = node("second", parent=root)
grandchild1 = node("grandchild", parent=child1)
print(grandchild1.get_bottom_up_ancestors())
print(grandchild1.get_top_down_ancestors())
OUTPUT
['grandchild', 'first', 'Top']
['Top', 'first', 'grandchild']

I don't quite understand what you are talking about but from what I DO understand there is no such thing as a parent object only parent classes so a node parent would not be possible

Related

Class Inheritance python require fill child class name

it might be a silly question since I'm new to Python.
However, I hope someone can explain this because I try to find lots of resource but still hardly understand the mechanism behind.
So I create a Parent Class and a Child Class. Parent Class have set_name, get_name method
In Child Class, I defined a new method call Turn_Uppercase which calling get_name then uppercase the name. And when using Turn_Uppercase Method, I have to filled in Child Class Name, otherwise it would not work.
Can someone explain the mechanism here!
Let's me explain in code:
So first I create a Parent Class with get_name and set_name method.
class Parent:
def __init__(self, text_input):
self.__name = ""
self.name = text_input
#property #this is get_name
def name(self):
return self.__name
#name.setter #this is set_name
def name(self, text_input: str):
if isinstance(text_input, str): # check input has to be string
self.__name = text_input
else:
print('Wrong type of data')
Then I create a Child Class with new method called Turn_uppercase
class Child_to_parent(Parent):
def __init__(self):
pass
def turn_uppercase(self):
return self.name.upper()
Now I put in some object and use Turn_uppercase Method, I have to fill in Child Class Name
test1 = Child_to_parent
test1.name = "abcdef" # using parent method
print(f'{test1.turn_uppercase(Child_to_parent)}') # using child method
When using parent method through property name, I don't need to declare Child Class Name, but when it comes to use Turn_uppercase Method then I have to.
Why it works this way?
This line makes all the difference
test1 = Child_to_parent
You are not creating an object here, instead merely assigning a reference to the class itself. What you must be doing is
test1 = Child_to_parent() #>Create the object!
test1.name = "abcdef"
print(f'{test1.turn_uppercase()}')
Now why it works with the class? It's because you attached a attribute to the class called name. The method you called used the class as argument and evaluated the class attribute!
Read this answer for a better understanding!

Trying to figure out how to pass variables from one class to another in python while calling a class from a dictionary

So I am getting used to working with OOP in python, it has been a bumpy road but so far things seem to be working. I have, however hit a snag and i cannot seem to figure this out. here is the premise.
I call a class and pass 2 variables to it, a report and location. From there, I need to take the location variable, pass it to a database and get a list of filters it is supposed to run through, and this is done through a dictionary call. Finally, once that dictionary call happens, i need to take that report and run it through the filters. here is the code i have.
class Filters(object):
def __init__ (self, report, location):
self.report = report
self.location = location
def get_location(self):
return self.location
def run(self):
cursor = con.cursor()
filters = cursor.execute(filterqry).fetchall()
for i in filters:
f = ReportFilters.fd.get(i[0])
f.run()
cursor.close()
class Filter1(Filters):
def __init__(self):
self.f1 = None
''' here is where i tried super() and Filters.__init__.() etc.... but couldn't make it work'''
def run(self):
'''Here is where i want to run the filters but as of now i am trying to print out the
location and the report to see if it gets the variables.'''
print(Filters.get_location())
class ReportFilters(Filters):
fd = {
'filter_1': Filter1(),
'filter_2': Filter2(),
'filter_3': Filter3()
}
My errors come from the dictionary call, as when i tried to call it as it is asking for the report and location variables.
Hope this is clear enough for you to help out with, as always it is duly appreciated.
DamnGroundHog
The call to its parent class should be defined inside the init function and you should pass the arguments 'self', 'report' and 'location' into init() and Filters.init() call to parent class so that it can find those variables.
If the error is in the Filters1 class object, when you try to use run method and you do not see a location or a report variable passed in from parent class, that is because you haven't defined them when you instantiated those object in ReportFilters.fd
It should be:
class ReportFilters(Filters):
fd = {
'filter_1': Filter1(report1, location1),
'filter_2': Filter2(report2, location2),
'filter_3': Filter3(report3, location3)
}
class Filter1(Filters):
def __init__(self, report, location):
Filters.__init__(self, report, location)
self.f1 = None
def run(self):
print(self.get_location())

Python: type inheritance without overloading method with the same name?

is it possible to achieve type inheritance without overwriting class methods? Take for example this code:
class Parent:
def special_method(self, name):
print("hello, {}!".format(name))
class Child:
def __init__(self, injected_parent):
self.parent = injected_parent
def special_method(self):
self.parent.special_method("Homer Simpson")
parent = Parent()
child = Child(parent)
child.special_method()
# hello, Homer Simpson!
Works as expected, but I want the type of child to be Parent and not Child:
print(type(child))
<class '__main__.Child'>
One way I've seen it done is to extend Child with:
class Child:
def __init__(self, injected_parent):
self.parent = injected_parent
self.__class__ = Parent
...
However, then child's special_method gets overwritten:
parent = Parent()
child = Child(parent)
child.special_method()
# TypeError: special_method() missing 1 required positional argument: 'name'
Any way to make child have type Parent without side-effects?
It seems like what you want to do is just regular inheritance, but as #juanpa.arrivillaga pointed out, you are using composition instead. What you are trying to do by changing the __class__ and overloading a method will not work. Here is an example using inheritance which will allow you to overload the method while still having access to the original method and to have child be an instance of parent which seems to meet your requirements:
class Parent:
def special_method(self, name):
print("hello, {}!".format(name))
class Child(Parent):
def special_method(self):
super().special_method("Homer Simpson")
child = Child()
Now, child will still have a type of Child, but with classes what you want to be checking instead is whether it is an instance of the superclass using isinstance:
>>> child.special_method()
hello, Homer Simpson!
>>> type(child)
<class '__main__.Child'>
>>> isinstance(child, Parent)
True

Overwriting parent function in multiple children

I have a couple of child classes that I want to use but they both inherit a method from their parent that doesn't quite behave the way I need it to.
class ParentClass():
def __init__(self, value):
self.value = value
def annoying_funct(self):
print("I don't do quite what's needed and print {0}".format(self.value + 1))
def other_parent(self):
print("I do other useful things my children inherit")
class Child1(ParentClass):
def __init__(self, value, new_value1):
super(Child1, self).__init__(value)
self.new_value1 = new_value1
def other_child1(self):
print("I do useful child things")
class Child2(ParentClass):
def __init__(self, value, new_value2):
super(Child2, self).__init__(value)
self.new_value2 = new_value2
def other_child2(self):
print("I do other useful child things")
I want to overwrite annoying_funct() as something like this:
def annoying_funct():
print("I behave the way I am needed to and print {0}".format(self.value))
ParentClass, Child1 and Child2 are from a very complex library (scikit-learn), so I want to keep all of my wrappers as thin as possible. What would be the cleanest/most pythonic way of getting the functionality of my two child classes while altering the parent class as needed?
My thoughts so far:
Create a new class that inherits from the parent which overwrites the function I don't like and then make wrapper classes for the children that inherit from the new class and the child classes.
class NewParentClass(ParentClass):
def annoying_funct(self):
print("I behave the way I am needed to and print {0}".format(self.value))
class NewChild1(NewParentClass, Child1):
pass
class NewChild2(NewParentClass, Child2):
pass
My areas of confusion:
Is this the correct approach? It seems a little weird and klugy. Is there a cleaner way of doing this?
Is the syntax used for the two child classes correct? It runs for me, but it seems a little weird having them do nothing but inherit and pass.
Is having my new parent inherit from the previous parent the correct way of doing this? The code runs for the children without the inheritance between parentClass and newParentClass (eg. def newParentClass():), but if someone tried to make an instance of newParentClass() the function wouldn't work because it uses attributes not present in that class (value). Is that ok if I assume the class will never be used?
There are a couple of ways to do what you ask.
Create a new class inheriting from the parent class, then use it as new parent class.
The only drawback for this solution is that the when you provide the new parent or child class to functions requiring the original Parent class might not work since they might use annoying_funct and rely on an annoying behavior.
class NewParentClass:
def annoying_funct(self):
print("I behave the way I am needed to and print {0}".format(self.value))
class NewChild1(NewParentClass):
pass
class NewChild2(NewParentClass):
pass
Manipulate the existing Parent class
This is the solution I would like to use since it destroys the annoying_funct completely by replacing it with a well behaving one, buy again, other methods and functions requiring the former annoying_funct might fail. The good side is, you don't need to create another parent class and children, so your code will be much more elegant.
class ParentClass():
...
def annoying_funct(self):
print("I don't do quite what's needed and print {0}".format(self.value + 1))
...
def well_behaving_func(s):
print("Good")
# Dynamically change the class method
ParentClass.annoying_func = well_behaving_func
class Child1(Parent):
pass
class Child2(Parent):
pass
c = Child1()
c.annoying_funct() # prints 'Good'
Add a well behaving new method to parent class before inheriting from it.
If you want to maintain the current behavior and don't want your existing code or packages dependent on the parent class break, you certainly should not overwrite the annoying_funct in the parent class. So you should define a well behaving function and use it in child classes.
class ParentClass():
...
def annoying_funct(self):
print("I don't do quite what's needed and print {0}".format(self.value + 1))
...
def well_behaving_func(s):
print("Good")
# Dynamically change the class method
ParentClass.well_behaving_func = well_behaving_func
class Child1(Parent):
pass
class Child2(Parent):
pass
c = Child1()
# use 'well_behaving_func', not the annoying one
c.well_behaving_func() # prints 'Good'
What I ended up doing was using the concept of a mixin to add my new functionality to the child classes. The resulting code was as follows:
class AnnoyMixin:
def well_behaving_func(self):
print("I behave the way I am needed to and print {0}".format(self.value))
class NewChild1(AnnoyMixin, Child1):
def annoying_funct(self):
return well_behaving_func(self)
class NewChild2(AnnoyMixin, Child2):
def annoying_funct(self):
return well_behaving_func(self)
Functionally, this mostly behaves the same as the code I had proposed in my question, but the modifications help with readability. First, by naming the new parent as a "Mixin" it makes it clear that this class is not designed to stand on its own, but rather is intended to add functionality to another class. Because of this, AnnoyMixin doesn't need to inherit from ParentClass, simplifying inheritance for the NewChild classes.
Second, rather than overwriting annoying_funct in AnnoyMixin, we create the new function well_behaving_func. It is then the NewChild classes job to overwrite annoying_funct by calling well_behaving_func. Functionally, this works more or less the same as if the AnnoyMixin had over written annoying_funct, but this way, it is clearer to those reading the code that annoying_funct is being overwritten in the NewChild classes.

recursively editing member variable: All instances have same value

I want to create a Tree data structure that consists of TreeNode objects. The root is a TreeNode. Each TreeNode has one parent TreeNode and a list of children TreeNodes.
The Tree is built up recursively. I simplified the code to make the example not too difficult. The function get_list_of_values_from_somewhere works correctly. The recursion ends when there are no child_values for a TreeNode and get_list_of_values_from_somewhere returns an empty list. That works perfectly well.
The children member of each TreeNode is not correct. The script collects all the TreeNodes in a list (node_list). There I can check that each TreeNode has a parent node and this parent node is correct.
But for some reason they all have the same list of childrens. I don't understand why. Everything else is correct. The recursion works, the TreeNodes are created correctly, their parent is correct. Why is their children list not filled correctly and how would you edit the memver variables of the instances after creating the instance?
class Tree(object):
def __init__(self, root_value):
print ("Creating tree")
self.root = self.createTree(root_value)
self.node_list = []
def createTree(self, value, parent=None):
node = TreeNode(value, parent)
children_values = get_list_of_values_from_somewhere()
for child_value in children_values:
child_node = self.createTree(child_value, node)
self.node_list.append(child_node)
node.children.append(child_node)
# I also tried alternatives:
#node.insertChildren(self.createTree(child_value, node))
#node.insertChild(child_node)
return node
class TreeNode(object):
def __init__(self, value, parent=None, children=[]):
self.value = value
self.parent = parent
self.children = children
def insertChildren(self, children=[]):
self.children += children
def insertChild(self, child):
self.children.append(child)
if __name__ == '__main__':
tree = Tree(1)
#tree.node_list contains a list of nodes, their parent is correct
#tree.root.children contains all children
#tree.node_list[x] contains the same children - although many of them should not even have a single child. Otherwise the recursion would not end.
Be very, very cautious of this:
def __init__(self, value, parent=None, children=[]):
and this:
def insertChildren(self, children=[]):
The initial value -- the list object created by [] -- is a single object which is shared. Widely.
You are using this single, shared, default list object widely.
You may want to use this instead.
def __init__( self, value, parent= None, children= None ):
if children is None: children= []
This technique will create a fresh, empty list object. No sharing.

Resources