Search code examples
pythonlistmemoryslicedel

Does using del with slicing creates a new object first and then deletes? Will it be in-place?


I want to know if slicing creates a copy of the list and if we use del over a slice. Then won't it create a copy, i.e. a new object from the list for a given slice first and then delete it?

So if a program is truly in-place then we cannot use slices.

# suppose if I use something like this:
a = [1,2,3,4,5,6,7,8,9,0]
del a[5:]

So, won't this del [a:] create a copy of a in the given slice a[5:] and then delete it?

Is so, it won't be an in-place operation right as we are using slices here.

But

a = [1,2,3,4,5,6,7,8,9,0]
for i in range(5,len(a),-1):
    del a[i]

This operation is in-place since we are not using slices.

So this should be faster right? Because we don't have to go through the pain of creating a new slice and directly deleting the object.

But I checked and it is the opposite:

%%timeit
a = [x for x in range(1000)]
del a[500:]
> 45.8 µs ± 3.05 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%%timeit
a = [x for x in range(1000)]
for i in range(999,499,-1):
    del a[i]
> 92.9 µs ± 3.24 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Now, if we assume that del a[500:] creates a slice first and then deletes, then it's not truly in-place, and using a for loop for the same task is in-place? But then why is the loop taking more time for the task?

Also, if in case, I assume that somehow they are both in-place and del a[500:] does not creates a slice copy of a at that index and del directly goes at the index 500 and recursively deletes the elements, then what is exactly a[500:] in this statement? Is it just to tell where should del delete from?

If so, then is it not a slice?

I referred to a few links such as the one below to find an answer to this, but I am not very clear from any of the explanations exclusively focusing on in-place operations.

What does "del" do exactly?


Solution

  • The important thing to understand is that these are two different things:

    del name
    

    and

    del container[index]
    

    The first deletes a name from the current namespace. del does not work with arbitrary expressions, and it does not delete objects, it delete names:

    >>> del [1,2,3]
      File "<stdin>", line 1
    SyntaxError: cannot delete literal
    >>> del list()
      File "<stdin>", line 1
    SyntaxError: cannot delete function call
    

    However,

    del container[index]
    

    Will invoke

    container.__delitem__(index)
    

    Which can do whatever you want, it can be in-place or not. Or not do anything...

    On the other hand

    container[index]
    

    Invokes

    container.__getitem__(index)
    

    Which, again, can do whatever you want. It can return a copy, or if you want to implement it that way, it can work in-place.

    Perhaps, this is enlightening:

    >>> class Container:
    ...     def __getitem__(self, item):
    ...         print('Container.__getitem__ called with', item)
    ...     def __delitem__(self, item):
    ...         print('Container.__delitem__ called with', item)
    ...
    >>> container = Container()
    >>> container[:]
    Container.__getitem__ called with slice(None, None, None)
    >>> del container[:]
    Container.__delitem__ called with slice(None, None, None)
    >>> del container
    

    Note, del container doesn't invoke either...