Implement and/or tree in python - python

I need to implement an And/or tree in python so I can evaluate boolean expressions,
I had an idea of creating a class that contains andNode, orNode and leafNode. The first two are are internal nodes that must have and or or values, the leafNode must have and integer value and represent the final leaves of the tree.I tried this but it doesn't seem to work:
class Node:
def __init__(self,leaf):
self.orNode = None
self.andNode = None
self.leaf = leaf
class and_or_tree (Node):
def __init__(self):
self.root=None
I need to test if an element exists in the tree, the height and iterate through it.

I think an example of such Leaf and and/or nodes could be something like this:
class Leaf:
def __init__(self, v):
self.val = v;
def __call__(self):
return self.val
class AndNode:
def __init__(self, l, r):
self.left = l;
self.right = r;
def __call__(self):
return self.left() and self.right()
class OrNode:
def __init__(self, l, r):
self.left = l;
self.right = r;
def __call__(self):
return self.left() or self.right()
You can build a tree like this:
print AndNode(Leaf(True), Leaf(False))()
print AndNode(OrNode(Leaf(True), Leaf(False)), Leaf(True))()
Which outputs this:
False
True

Related

Using comparison operator on custom objects

I have the following code:
from typing import Optional
class Tree:
class Tree:
def __init__(self):
self.root: Optional[Node] = None
def __str__(self) -> str:
return f"{self.root.data}"
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
I have this class. I am trying to find maximum data element in my tree. The algorithm I created keeps a temporary element and updates the temporary element if it finds a value greater than this element. At first, I make my temporary element value 0 and then do the following:
if tree.data > temp:
temp = tree.data
but I get the following error:
TypeError: '>' not supported between instances of 'Node' and 'Node'
How can I use the ">" operator in this situation?
Give an implementation of the __gt__ dunder method for the Node object. This tells Python what basis to compare Node objects on:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def __gt__(self, other):
return self.data > other.data

Recursion within a method

In my code I am counting the number of nodes in a complete binary tree and I decided to use a recursive approach. However, when I call my method within itself, I get NameError: name 'countNodes' is not defined.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def countNodes(self, root: TreeNode, count = 0) -> int:
if not root.left and not root.right:
return 1
else:
count += countNodes(self, root.left, count) + countNodes(self, root.right, count)
To answer this question.
When accessing a method of the class instance inside the class you have to use self.method. So in your case you have to use self.countNodes instead of countNodes.

Is there a way to define a class instance is None?

For example, when I have such a Node class defined.
class Node:
def __init__(self, val=None, next=None):
self.val = val
self.next = next
def __bool__(self):
return self.val is not None
When I initialize it with empty arguments, like below. Is there a way to self-define method to say a is None?
a = Node()
a is None # False, but can it be true if I want?
While you cannot override the is comparison, you can at least override the equality operator if you want to quickly check up whether a specific parameter (or condition) within your class should yield True on comparison, e.g.:
class Node:
def __init__(self, val=None, next=None):
self.val = val
self.next = next
def __eq__(self, obj):
return obj == self.val
n = Node()
print(n == None) # True
n = Node(5)
print(n == None) # False
No, but...
You cannot override the is, and, or or operators.
Defining __bool__ allows you to write statements like
class Node:
def __init__(self, val):
self.val = val
def __bool__(self):
return self.val is not None # <--- added "return"
for val in (0, 1, True, None):
n = Node(val)
# These three are equivalent
if n:
assert n.__bool__()
assert n.val is not None
# These three are equivalent
else:
assert not n.__bool__()
assert n.val is None
https://docs.python.org/3/reference/datamodel.html#object.bool
This may not do exactly what you want but you could overwrite the __new__ class method so that, when the class constructor is called with no arguments, the None object is returned instead of an instance of Node.
I think this should work (my metaclass knowledge is spotty).
class Node:
def __new__(cls, val=None, next=None):
if val is None and next is None:
return None
return super().__init__(cls, val, next)
def __init__(self, val, next):
if self is None:
return
...
It is my duty to recommend that you not go down this route, however. Fiddling with __new__ is tricky and dangerous and is probably more trouble than it's worth.

Convert string to a linkedlist in Python

This is how I am defining my linkedList
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
I am trying to convert a string to a linkedList
stringTotal = "abc"
head = stringToListNode(stringTotal)
#this method should return a -> b -> c
def stringToListNode(stringTotal):
for i in stringTotal:
currentNode = ListNode(i)
How can I get the next letter of the string and make it the next node?
Try this:
def stringToListNode(stringTotal):
previousNode = None
first = None
for i in stringTotal:
currentNode = ListNode(i)
if first is None:
first = currentNode
if previousNode is not None:
previousNode.next = currentNode
previousNode = currentNode
return first
One nice way to do this might be to define a from_string classmethod on your ListNode class that will recursively build a linked list for you and return the head:
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
#classmethod
def from_string(cls, s):
if s:
n = cls(s[0])
n.next = ListNode.from_string(s[1:])
return n
n = ListNode.from_string('hello')
print(n.next.next.next.next.val)
>>> 'o'
You can create an insert method as an attribute of ListNode, that can be called on the next attribute should that latter already store a node of ListNode:
class ListNode(object):
def __init__(self, x=None):
self.val = x
self.next = None
def insert(self, val):
if self.val is None:
self.val = val
else:
getattr(self.next, 'insert', lambda x:setattr(self, 'next', ListNode(x)))(val)
def __str__(self):
return '{}, {}'.format(self.val, str(self.next) if self.next else '')
def __repr__(self):
return 'List(<{}>)'.format(str(self))
#classmethod
def insert_vals(cls, s):
l = cls()
for i in s:
l.insert(i)
return l
_list = ListNode.insert_vals('abc')
print(_list)
Output:
List(<a, b, c, >)
Note, however, that the operation accomplished in method insert can also be performed as a simple function, however, it is not as clean as an instance attribute:
class ListNode(object):
def __init__(self, x=None):
self.val = x
self.next = None
def __str__(self):
return '{}, {}'.format(self.val, str(self.next) if self.next else '')
def __repr__(self):
return 'List(<{}>)'.format(str(self))
def insert_val(_l:ListNode, value:str) -> None:
if _l.val is None:
_l.val = value
else:
if isinstance(_l.next, ListNode):
insert_val(_l.next, value)
else:
_l.next = ListNode(value)
_l = ListNode()
for i in 'abc':
insert_val(_l, i)
>>>_l
Output:
List(<a, b, c, >)

python object has properties on initialze

I created a graph node class in Python.
Each node has single parent, multiple children and properties.
An implementation should be like below:
# graph_test.py
class Node(object):
def __init__(self, name, prop={}):
self.name = name
self.properties = prop
self.parent = None
self.children = []
print "New node:", self.name, self.properties
def add_prop(self, k, v):
self.properties.update({k:v})
print "added prop:", k, v
def add_child(self, n):
self.children.append(n)
n.parent = self
class Foo(object):
def __init__(self, n):
self.node_num = n
self.root_node = None
self.current_node = None
def bas(self):
n = Node("root")
n.add_prop("this_prop_is", "set_only_root_node")
self.root_node = n
return self.root_node
def bar(self):
self.current_node = self.bas()
for i in range(self.node_num):
n = Node(str(i))
self.current_node.add_child(n)
self.current_node = n
if __name__ == '__main__':
f = Foo(5)
f.bar()
In this code, it is expected that only the root node has the property whose key is "this_prop_is".
However, result of execution is like below:
$ python ./graph_test.py
New node: root {}
added prop: this_prop_is set_only_root_node
New node: 0 {'this_prop_is': 'set_only_root_node'}
New node: 1 {'this_prop_is': 'set_only_root_node'}
New node: 2 {'this_prop_is': 'set_only_root_node'}
New node: 3 {'this_prop_is': 'set_only_root_node'}
New node: 4 {'this_prop_is': 'set_only_root_node'}
All nodes have the same key even I add it to only node "root".
I use python 2.7.6.
My questions are:
Is this a bug?
If this is not a bug, why does this occur?
How to fix this issue?
This is not a bug. The problem is your default value for prop. You set it as an empty dictionary. However, this empty dictionary is copied by reference with self.properties = prop and when it is modified, the next time a new Node is created, the modified dictionary is used as the default value.
To fix this, put None as the default value and check for None when assigning properties:
# graph_test.py
class Node(object):
def __init__(self, name, prop=None):
self.name = name
self.properties = prop or {}
self.parent = None
self.children = []
print "New node:", self.name, self.properties
def add_prop(self, k, v):
self.properties.update({k:v})
print "added prop:", k, v
def add_child(self, n):
self.children.append(n)
n.parent = self
class Foo(object):
def __init__(self, n):
self.node_num = n
self.root_node = None
self.current_node = None
def bas(self):
n = Node("root")
n.add_prop("this_prop_is", "set_only_root_node")
self.root_node = n
return self.root_node
def bar(self):
self.current_node = self.bas()
for i in range(self.node_num):
n = Node(str(i))
self.current_node.add_child(n)
self.current_node = n
if __name__ == '__main__':
f = Foo(5)
f.bar()
This is because you have a mutable default value in Node.__init__. In Python, default values are determined when the function is created, and the same instance will always be used. So every time you create a new Node and don't give it an explicit prop argument, it will use the same dictionary.
This is often solved by using None as the default value and creating a new dictionary each time inside the function if the argument is None, for example by doing self.properties = prop or {}. (This will also use a new dictionary if you give it an empty dictionary, but this isn't usually a problem)
Change props={} to props=None and self.properties = prop to self.properties = prop or {}
This is due to the behavior of mutable default arguments in Python. Here's a good resource to read up on this: http://effbot.org/zone/default-values.htm

Categories

Resources