global name is not defined error for static method - python

For the code below I wrote for Binary Search Tree, I get the error delete_node(curr,value)
NameError: global name 'delete_node' is not defined. I am wondering why this will be the case for static method which is called from the instance method delete(self,value) . Why does it not work as a static method? I am calling it from a instance method 'delete' so does it does not require access to instance members explicitly.
class Node:
def __init__(self,value=None):
self.value = value
self.left = None
self.right = None
class BST:
def __init__(self,value=None):
self.root = Node(value)
#staticmethod
def delete_node(node,value):
if node is None:
return node
if value > node.value:
node.right = delete_node(node.right,value)
elif value < node.value:
node.left = delete_node(node.left,value)
else:
if node.left is None and node.right is None:
node = None
return None
elif node.left is None:
temp = node.right
node = None
return temp
elif node.right is None:
temp = node.left
node = None
return temp
else:
#min_value = get_min(node.right)
node.value = get_min(node.right)
node.right = delete_node(node.right,node.value)
def delete(self,value):
if self.root == None:
return
curr = self.root
delete_node(curr,value)
def get_min(node):
curr = node
while curr.left:
curr = curr.left
return curr.value
if __name__ == '__main__':
tree = BST(5)
tree.delete(5)

As others have eluded to, in order to call methods on a class object you have to specify the thing you're calling it on.
So where you use delete_node, you can use BST.delete_node since it's static. The design is a bit odd given a BST would normally be done as an object encapsulated completely, but this is your immediate problem.
If it's an instance method, then you would call self.delete_node

The design of your class seems confusing.
In python, when we define a staticmethod that means the class name is just something like namespace, you can not access any member of the class in the staticmethod.
If you want static methods in C#, try classmethod.
I recommend the popular question for classmethod vs staticmethod.
In the specific question, change your code like this:
class BST:
...
#staticmethod
def delete_node(node,value):
...
node.right = BST.delete_node(node.right,node.value)
def delete(self,value):
...
BST.delete_node(curr,value)

Related

AttributeError: type object 'BSTNode' has no attribute 'left'

I was trying to construct a Binary Search Tree in python.
This is my node class:
class BSTNode:
def __init__(self,val):
self.left = None
self.right = None
self.val = val
This class contains a function called printTree, which is supposed to print the tree inorder. Here is the printTree function:
def printTree(self,val):
if self.left is not None:
self.left.printTree()
print(self.val)
if self.right is not None:
self.right.printTree()
When I execute the function it gives AttributeError: type object 'BSTNode' has no attribute 'left'
Here is my full code:
class BSTNode:
def __init__(self,val):
self.left = None
self.right = None
self.val = val
def insertNode(self,val):
if self.val:
if val < self.val:
if self.left is None:
self.left = BSTNode(val)
else:
self.left.insertNode(val)
else:
if self.right is None:
self.right = BSTNode(val)
else:
self.right.insertNode(val)
else:
self.val = val
def findval(self,fval):
if (fval == self.val):
print(str(self.val)," data found ")
elif(fval < self.val):
if self.left is None:
print(str(self.val)," data not found")
else:
self.left.findval(fval)
else:
if self.right is None:
print(str(self.val)," data not found")
else:
self.right.findval(fval)
def printTree(self,val):
if self.left is not None:
self.left.printTree()
print(self.val)
if self.right is not None:
self.right.printTree()
root = BSTNode(12)
root.insertNode(6)
root.insertNode(5)
root.insertNode(18)
root.insertNode(15)
root.insertNode(21)
BSTNode.printTree(BSTNode)
You are calling printTree() without arguments:
self.left.printTree()
...
self.right.printTree()
Yet, you defined it to accept val, which is just unused by the way:
def printTree(self,val):
Replace it to:
def printTree(self):
The method printTree() is an instance method, not a #classmethod nor #staticmethod. That means it requires an active instance/object of BSTNode to be called which will be passed as the self argument. So this call is incorrect:
BSTNode.printTree(BSTNode)
It must be:
root.printTree(BSTNode)
Then considering my point-1 above, finally it should be:
root.printTree()
Where root is your current active instance of type BSTNode.
After those fixes, it would be successful
5
6
12
15
18
21
Alternative Solution
If you don't want printTree() to be an instance method, make it a #staticmethod instead.
class BSTNode:
...
#staticmethod
def printTree(self): # I named it self to be close to your real implementation. Ideally, rename it to something like "node" or "obj" to avoid confusion since this is not an instance method.
if self.left is not None:
self.printTree(self.left)
print(self.val)
if self.right is not None:
self.printTree(self.right)
...
BSTNode.printTree(root)
This will produce the same output.

Binary search tree python with OOP

I'm trying to understand python and OOP along with data structures
I'm now looking at the implementation of a binary search tree
here is the class for the node structure
class Node():
def __init__(self, data):
self.data = data
self.leftChild = None
self.rightChild = None
the developer of this code has created the insert feature in Node class and in another class called tree
here is what it looks like in the node class :
def insert(self, data):
if self.data == data:
return False
elif data < self.data:
if self.leftChild:
return self.leftChild.insert(data)
else:
self.leftChild = Node(data)
return True
else:
if self.rightChild:
return self.rightChild.insert(data)
else:
self.rightChild = Node(data)
return True
however, he created a function with the same name in the tree class which looks like this
class Tree():
def __init__(self):
self.root = None
def insert(self, data):
if self.root:
return self.root.insert(data)
else:
self.root = Node(data)
return True
I do have some questions at this point, why are there 2 functions with the same name? and when I try to execute one of them without the other on this code it shows an error
if __name__ == '__main__':
tree = Tree()
tree.insert(10)
why does he made instance for tree not for node ?
can someone please explain those concepts for me, thanks!
When you creating tree object it doesn't creating any node. It's just defining a root variable with None. Node is creating when you call tree.insert() method.
insert() method in Tree class is checking if root is None it creating a new Node. Otherwise it calling the insert method in Node class.
Why does he made instance for tree not for node ?
Because insert() method in Node class only handles the case where tree has atleast one node or tree is not empty. That's why he creating the instance of Tree.
Code with insert() method in Tree class:
class Node():
def __init__(self, data):
self.data = data
self.leftChild = None
self.rightChild = None
class Tree():
def __init__(self):
self.root = None
def insert(self, root, data):
if root is None:
self.root = Node(data)
elif root.data < data:
if root.rightChild is None:
root.rightChild = Node(data)
else:
self.insert(root.rightChild, data)
else:
if root.leftChild is None:
root.leftChild = Node(data)
else:
self.insert(root.leftChild, data)

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.

What is the easiest way to turn a LinkedList class into a Circular Linked List class?

I have a LinkedList class that has close to 200 lines of code. I would like to make a new class LLCircular(LinkedList) by always making sure that any myLL.tail.next is myLL.head. I believe I would need to update append(), push(), remove(), etc. accordingly. Is there a way I can do this to keep the original LinkedList class intact? Maybe a decorator or some dunder method?
For brevity's sake, if reading the code, my push() method is just the inverse of append(). I also have a pop() and remove() method, which would need to be updated, if I just rewrite those methods. As I am trying to avoid that approach, I am not posting that part of the code.
class LinkedListNode:
def __init__(self, value, nextNode=None, prevNode=None):
self.value = value
self.next = nextNode
self.prev = prevNode
def __str__(self):
return str(self.value)
class LinkedList:
def __init__(self, values=None):
self.head = None
self.tail = None
if values is not None:
self.append(values)
def __str__(self):
values = [str(x) for x in self]
return ' -> '.join(values)
def append(self, value=None):
if value is None:
raise ValueError('ERROR: LinkedList.py: append() `value` PARAMETER MISSING')
if isinstance(value, list):
for v in value:
self.append(v)
return
elif self.head is None:
self.head = LinkedListNode(value)
self.tail = self.head
else:
''' We have existing nodes '''
''' Head.next is same '''
''' Tail is new node '''
self.tail.next = LinkedListNode(value, None, self.tail)
self.tail = self.tail.next
if self.head.next is None:
self.head.next = self.tail.prev
return self.tail
'''class LLCircular(LinkedList):'''
''' ??? '''
Test Code:
foo = LinkedList([1,2,3])
foo.tail.next = foo.head #My LL is now circular
cur = foo.head
i = 0
while cur:
print(cur)
cur = cur.next
i +=1
if i>9:
break
What you want is to call your LinkedList base class functions using the super keyword, and then add the slight modifications to the LLCircular class function, i.e:
class LLCircular(LinkedList):
def append(self, value=None):
super(LLCircular, self).append(value)
# In addition to having called the LinkedList append, now you want
# to make sure the tail is pointing at the head
self.tail.next = self.head
self.head.prev = self.tail
If it is "circular" it won't need a tail or head, would it?
Nor "append" makes sense - insert_after and insert_before methods should be enough - also, any node is a reference to the complete circular list, no need for different objects:
class Circular:
def __init__(self, value=None):
self.value = value
self.next = self
self.previous = self
def insert_after(self, value):
node = Circular(value)
node.next = self.next
node.previous = self
self.next.previous = node
self.next = node
def insert_before(self, value):
node = Circular(value)
node.next = self
node.previous = self.previous
self.previous.next = node
self.previous = node
def del_next(self):
self.next = self.next.next
self.next.previous = self
def __iter__(self):
cursor = self.next
yield self
while cursor != self:
yield cursor
cursor = cursor.next
def __len__(self):
return sum(1 for _ in self)

Removing a node from a linked list with "del" doesn't work

The problematic part is in the function remove. Even after del is called, the relevant node is not removed from the Linked List. Did I misunderstand something about del?
class Node:
def __init__(self, val):
self.val = val
self.next = None
def add(self, val):
if not (self.next):
self.next = Node(val)
else:
self.next.add(val)
def remove(self, val):
if self.val == val:
if self.next:
self.val = self.next.val
self.next = self.next.next
else:
del self # this doesn't remove the node from linked list
else:
if self.next:
self.next.remove(val)
else:
print "no such val found %d" % val
def __str__(self):
output = []
while self is not None:
output.append(str(self.val))
self = self.next
return " -> ".join(output)
head = Node(1)
head.add(2)
print head
head.remove(3)
head.add(3)
head.add(4)
print head
head.remove(3)
head.remove(4)
print head
The statement del self only removes the name self from the local scope (and decrements the reference count). It has no effect on the other references to it nor to those objects.
To remove the node from the linked list, you must update the node(s) that refer to it. Since you have a singly linked list, you must traverse the list from the beginning to find the node whose node.next == self, then change it to node.next = self.next to remove self from the sequence of links.

Categories

Resources