2

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?

Zenquiorra
  • 140
  • 1
  • 2
  • 13

2 Answers2

1

then what is exactly a[500:] in this statement? Is it just to tell where should del delete from?

Yes.

Then won't it create a copy, i.e. a new object from the list for a given slice first and then delete it?

No copy is created. That's why del is a keyword, rather than a built-in function: it's special syntax that interprets the a[5:] part differently in context. (If it were a function, then a[5:] would have to be evaluated before the function could be called; and also the function couldn't possibly work, because it would be provided with a separate value rather than knowing which original object to operate on.)

But I checked and they both take almost equal time:

for i in range(500,1000,-1):

Careful; this is an empty loop. You presumably intended for i in range(1000, 500, -1), but that is not correct either - it should be for i in range(999, 499, -1) to get the matching behaviour. And now you understand why the slice syntax is supported for del :)

No idea about your timing results, anyway.

Karl Knechtel
  • 62,466
  • 11
  • 102
  • 153
1

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...

juanpa.arrivillaga
  • 88,713
  • 10
  • 131
  • 172