0

I created a small function funct which should assign None to node if a value of -1 is passed else assign the value to the value attribute of the node object. I created a simple binary tree [root, left, right] and finally print the nodes.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def funct(node, val):
    if val == -1:
        ### modify code here 
        node = None
    else:
        node.val = val

root = TreeNode()
root.val = 'root'
root.left = TreeNode()
root.right = TreeNode()

funct(root.left, 'left')
funct(root.left, -1)

print(root, root.val)
print(root.left, root.left.val)
print(root.right, root.right.val)

When I print the nodes I see the following output.

output

The right node is in memory and is not None.

How do I assign None to the orignal object by modifying the code inside the if in funct to get the following output instead.

esentially simulating the following code and getting the output :

root = TreeNode()
root.val = 'root'
root.left = TreeNode('left')
root.right = None

output2

Note : I can change my algo. to create a new node only when val != -1. However I want to understand if there is a way to modify a passed object it in pyhton.

Edits : removed the word "delete". Added some clarification.

  • You cannot delete objects in Python. You can remove / replace references to those objects, though – juanpa.arrivillaga May 19 '22 at 20:28
  • "I know I can change my algo. to create a new node only when val != -1. However I want to understand if there is a way to modify a passed object without rebinding it in pyhton." huh? You can mutate an object trivially. One thing *that absolutely does not modify an object*, though, is rebinding. So I think you have a fundamental misunderstanding – juanpa.arrivillaga May 19 '22 at 20:29
  • "I created a small function funct which should assign None to node (delete the node" *NO*. Assigning `None` to a variable simply assigns `None` to a variable, it doesn't delete anything. This is *crucial* to understand. Read the following: https://nedbatchelder.com/text/names.html – juanpa.arrivillaga May 19 '22 at 20:30
  • So TL:DR Python exposes **no way** to delete an object, Python is a fully memory-managed language. – juanpa.arrivillaga May 19 '22 at 20:32
  • @juanpa.arrivillaga Thanks for th clarificaiton. I apologise for the confusion I edited my question to add clarity. What I want to understand is can I change the object pointed to by root.right and make it point to None instead. If that makes sense ? – Rishabh Bhatt May 19 '22 at 20:38
  • 1
    Furthermore, what you are asking about is **nothing to do with** "deleting" objects. What you are actually asking about is how to replace the caller's variable, so that `root` becomes `None` after the function is called. You **cannot do that, either** from within the function, as also explained in the link above. You can only *modify* the object that was passed in. Your function *does not receive* names. It receives *values*. – Karl Knechtel May 19 '22 at 20:39
  • 1
    "can I change the object pointed to by root.right and make it point to None instead." Yes, but you have to pass the root itself (so that you can assign to its *attribute*), not the `root.right` value. It would work the same way as the assignment to `root.val` currently works within that function. This means that you will need some way, when calling the function, to know whether it is the left child or the right child that should be modified. – Karl Knechtel May 19 '22 at 20:41
  • See also https://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference – Karl Knechtel May 19 '22 at 21:05
  • 1
    @RishabhBhatt you can *if you have a reference to `root` by doing `root.right = whatever`*. – juanpa.arrivillaga May 19 '22 at 21:18

2 Answers2

2

I created a small function funct which should assign None to node if a value of -1 is passed else assign the value to the value attribute of the node object.

funct cannot replace the node that was passed to it (i.e.: make one of the caller's variables refer to a new node instead of this one), because it receives the node, and not any variable name for that node.

It can modify the node that was passed to it. In particular, it can replace the child values, by reassigning to node.right and node.left. This means "the .right and .left of the TreeNode instance that was passed to me". node is a name that funct uses for that passed-in instance, that has nothing to do with the calling code in any way.

To "remove" a subtree, then, we need to pass the parent of the tree that will be removed, and also indicate which side will be removed. That might look like:

def remove_child(parent, which):
    if which == 'left':
        parent.left = None
    elif which == 'right':
        parent.right = None
    else:
        raise ValueError("invalid child name")

The root of the tree has no parent, so removing the entire tree must be treated as a special case.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
  • What if I do '''del node''' for the passed child node inside '''funct'''. What happens to the orignal object root.left / root.right. – Rishabh Bhatt May 19 '22 at 20:54
  • 2
    `del` **does not in any way** cause objects to be destroyed, deleted, or in any way modified. It operates on **the name** (or dict key, or list element), **not** the value. `del node` just means "okay, `node` is no longer a name you can use, until you assign to it again". The node that it referred to is still there. – Karl Knechtel May 19 '22 at 21:00
  • 1
    If in the above code I wrote `del parent.left` instead of `parent.left = None`, then that node would no longer have a `left` attribute, and trying to get its `left` would raise an `AttributeError`. *Either way* would **allow** the subtree to be garbage-collected, **only if** there are **no** other references to it. That's what garbage collection is, after all. It cleans up things that can no longer be referred to, and only those things. – Karl Knechtel May 19 '22 at 21:02
  • @RishabhBhatt `del node` deletes the *local* variable from the local namespace. It *does nothing to any objects directly* (of course, it removes a reference to an object, and if an object is no longer referenced by anything, then it is unreachable and free to be garbage collected) – juanpa.arrivillaga May 19 '22 at 21:20
0

Not sure if this is what you need, but I think its doing the behavior you want:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

    def funct(self, child: str, val):
        if val == -1:
        ### modify code here 
            setattr(self,child, None)
    
        else:
            setattr(self,child,TreeNode(val))


root = TreeNode()
root.val = 'root'
root.left = TreeNode()
root.right = TreeNode()

root.funct(child='left', val='left')
root.funct(child='right', val='right')

print(root, root.val)
print(root.left, root.left.val)
print(root.right, root.right.val)
print(None)

root.funct(child='left', val= -1)
print("\n")
print(root, root.val)
# print(root.left, root.left.val) - you cannot print root.left.val because None has no attribute
print(root.left)
print(root.right, root.right.val)
Licnep
  • 13
  • 4