Search code examples
pythondata-structuresdel

I want to know exactly about Python del reserved words


I want to know exactly about Python del reserved words. In the process of studying linked list, I got curious about del reserved words. First, common frame code.

class Node:
    def __init__(self, data, nxt=None):
        self.data = data
        self.nxt = nxt
        
class NodeMgmt:
    def __init__(self, data):
        self.head = Node(data)
        
    def add(self, data):
        current_node = self.head
        while current_node.nxt is not None:
            current_node = current_node.nxt
        current_node.nxt = Node(data)
        
    def desc(self):
        current_node = self.head
        while current_node:
            print(current_node.data)
            current_node = current_node.nxt

I don't understand the result value of this code

    def delete(self,data):
        current_node = self.head          
        while current_node.data != data:
            current_node = current_node.nxt
        del current_node
lnk = NodeMgmt(1)
lnk.add(2)
lnk.desc() >>> 1 2
lnk.delete(2)
lnk.desc() >>> 1 2

I know the following code is correct.

   def delete(self,data):
        current_node = self.head
        current_node_parent = self.head
        while current_node:
            if current_node.data != data: 
                current_node_parent = current_node
                current_node = current_node.nxt
            else:
                break
                
        current_node_parent.nxt = current_node.nxt

I would like to point out clearly for the flexible use of del

li = [1,2,3]
li1 = li
li1.append(1)
print(li)
del li1[2:] 
print(li) # >>> [1,2]
del li1
print(li) # >>> [1,2] Why isn't this deleted?

Solution

  • Here we first describe the useage of the del statement to answer the title:

    1. When directly acting on a variable name (del some_object), it will delete the binding between the variable name and the object:
    >>> foo = 1
    >>> globals()
    {'__name__': '__main__',
     '__doc__': None,
     '__package__': None,
     '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000025FF6957C40>,
     '__spec__': None,
     '__annotations__': {},
     '__builtins__': <module 'builtins' (built-in)>,
     'foo': 1}
    >>> del foo
    >>> globals()
    {'__name__': '__main__',
     '__doc__': None,
     '__package__': None,
     '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000025FF6957C40>,
     '__spec__': None,
     '__annotations__': {},
     '__builtins__': <module 'builtins' (built-in)>}
    

    There is no direct relationship with garbage collection here. It just deletes the key value pair with foo as the key from the namespace, and decreases the reference count of the object bound before foo by one. When the reference count of an object decreases to 0, it will be collected as garbage.

    1. When used for subscript (del some_object[subscript]), it delegates the deletion behavior to the object's __delitem__ method:
    >>> class Foo:
    ...    def __delitem__(self, item):
    ...        print('deleting', item)
    ...
    >>> foo = Foo()
    >>> del foo[0]
    deleting 0
    >>> del foo[:]
    deleting slice(None, None, None)
    
    1. When used for attribute (del some_object.attribute), it delegates the deletion behavior to the object's __delattr__ method:
    >>> class Foo:
    ...     def __delattr__(self, item):
    ...         print('deleting', item)
    ...
    >>> foo = Foo()
    >>> del foo.attr
    deleting attr
    >>> del foo.foo
    deleting foo
    

    Next is the answer to the question in the description:

    I don't understand the result value of this code

        def delete(self,data):
            current_node = self.head          
            while current_node.data != data:
                current_node = current_node.nxt
            del current_node
    
    lnk = NodeMgmt(1)
    lnk.add(2)
    lnk.desc() >>> 1 2
    lnk.delete(2)
    lnk.desc() >>> 1 2
    

    What you do here is: iterate the linked list, find the target node and bind the current_node to it, finally unbind the current_node to it. The whole process will not affect the linked list and target node, only the namespace. (and even if the del statement can successfully reclaim the node, the linked list will be disconnected from the middle.)

    I would like to point out clearly for the flexible use of del

    li = [1,2,3]
    li1 = li
    li1.append(1)
    print(li)
    del li1[2:] 
    print(li) # >>> [1,2]
    del li1
    print(li) # >>> [1,2] Why isn't this deleted?
    

    This involves the use of two different del statements:

    • del li1[2:] delegates the deletion to the list itself. Python's implementation is to delete the elements in the list within the slice range.
    • del li1 only unbinds li1 from the list, because li still binds the list, it will not be collected as garbage. Only when you also unbind the list from li will the list be collected. At the same time, because there is no longer a variable name bound to it, you cannot access the list that has been collected.