For a collection like a binary tree it is best, for consistency (for a binary tree, 'has left and right links' is a fairly strong interface contract obligation), to only clear the target of a link and not the link itself. It adds additional complexity to iterators during future maintenance if one must check for nonexistent attributes in addition to the target of the link being empty.
For this case, a binary tree, it is better to just set the target of the link to None with either self.left=None or self.right=None when children do not exist in their respective slots.
In contrast, one possible use for del is when a local variable needs to hold some temporary (emphasis on temporary) value whose scope should only be the next few lines or so. We could use a nested function (or in this case creating a general purpose swap method would be preferred) but in a pinch we can just del the temporary when we are done with it:
# swap two things
temp=collection[a]
collection[a]=collection[b]
collection[b]=temp
del temp
If we don't use del temp there is a second reference to the thing at collection[a] and we might get surprised down the line in the case we don't use temp later in the current scope but do expect some backend ref-counter to remove the thing in collection[a] after del collection[a], collection.pop(a,None) (discarding the return value) or collection[a]=None but the backend still registers that thing still exists (because it does, in temp).
self.left=Noneandself.right=Noneclear the targets of the links.