What ways can we use to remove elements from a list in Python?

The following addresses that and related issues.

Let’s consider the difference between identity and equivalence in Python.

In Python, objects are identical if they are, in fact, the same object.

Objects are considered to be equivalent if they have the same value. There are some minor exceptions that recognize equivalence of values that are similar, but not of the same type. See the end of this post for an example.

If objects are identical, they are also equivalent. However, if they are equivalent, they are not necessarily also identical. The following may help clarify the difference between identity and equivalence regarding Python lists.

To help with the discussion, we will be using the following operators:

  • is: tests for identity
  • ==: tests for equivalence

Let’s create two lists with the same content. They will be equivalent, but not identical.

>>> a = [1, 2, 3, 4, 5, 6, 7]
>>> b = [1, 2, 3, 4, 5, 6, 7]
>>> a == b # are they equivalent?
True
>>> a is b # are they identical?
False

With lists, the = operator assigns a reference to the list specified by the expression on the right side of the operator to a variable on the left side of the operator. Let’s demonstrate that by creating a list, assigning it to c, then assigning c to d. This will create only one list, Both c and d will refer to that same list. Therefore, c and d will be identical and also equivalent.

>>> c = [1, 2, 3, 4, 5, 6, 7]
>>> d = c
>>> c == d
True
>>> c is d
True

Taking a slice of a list creates a new list. We can use this as a means of making a copy of a list. Let’s try that with two variables, e, and f.

>>> e = [1, 2, 3, 4, 5, 6, 7]
>>> f = e[:] # this slice includes all of the elements in e
>>> e == f
True
>>> e is f
False

Recall that a and b are equivalent, but not identical. Let’s modify a and find out whether that affects b.

>>> a.append(8) # 8 gets appended to a, but not to b
>>> a
[1, 2, 3, 4, 5, 6, 7, 8]
>>> b
[1, 2, 3, 4, 5, 6, 7]

b was not affected.

Recall that c and d are identical. Let’s modify c and find out whether that affects d.

>>> c.append(8) # 8 gets appended to c, which affects d
>>> c
[1, 2, 3, 4, 5, 6, 7, 8]
>>> d
[1, 2, 3, 4, 5, 6, 7, 8]

Modifying c modified d, because both variables refer to the same list.

When a list is passed to a function as an argument, the corresponding function parameter will refer to the same list as the argument. If we wish to work with a copy of the list instead of with the original, we must make a copy of the list. Within the above examples, we used a slice to assign a copy of e to f. Alternatively, we could have used the list function to create the copy, as follows:

>>> f = list(e)
>>> e == f
True
>>> e is f
False

As promised above, here is an equivalence between lists that contain items that differ in type:

>>> [1.0, 0.0, 1.0] == [True, False, True]
True

Edited on September 6, 2019 to modify the final example

2 Likes

Please correct me if i am wrong.

Your mention of first instance (or may me i did not understand what you meant) as quoted above sound a bit confusing. I tried it (see below code) by removing the element in index 1 , and it worked.


In [13]: list = [1, 2, 3, 1]                                                    

In [14]: list.remove(2)                                                         

In [15]: print(list)                                                            
[1, 3, 1]


My understanding of your first instance is that the remove() function take out the first element or element in index 0 in the list.

Kindly help me learn more

Consider,

>>> lst = [1, 2, 3, 1, 2, 3, 1, 2, 3]
>>> lst.remove(1)
>>> lst
[2, 3, 1, 2, 3, 1, 2, 3]
>>> 

The fact that 1 is found at the first index has no bearing, only that it is the first instance of that value.

>>> lst.remove(1)
>>> lst
[2, 3, 2, 3, 1, 2, 3]
>>> 

By implication, first instance here means the first 1 that appears in the element, right?

as in the first element to contain a 1. We are searching for values, regardless of index. So, yes, instance refers to value.

1 Like

Thank you very much mtf

1 Like

This was a super helpful reminder as I’m trying to understand where I was going wrong using .remove().
Thank-you!

1 Like

After reading through the thread and giving myself the time to focus on the answer, I get it. I was stuck at: lst = lst[1:] - understanding why it was done this way versus using .remove() or .pop() on the first item of the list ( item at index [0]).
Thank-you to those who asked questions and those who provide such thorough answers!

1 Like

def delete_starting_evens(lst):
while len(lst) > 0 and lst[0] % 2 == 0:
lst.pop(0)
return lst

Can someone explain to me why the code above with the return function is correct while the code on the bottom is incorrect?

def delete_starting_evens(lst):
while len(lst) > 0 and lst[0] % 2 == 0:
lst.pop(0)
print(lst)

Hello, @bit2637307750.

From the instructions:
image

return hands a value back to the caller, and terminates the function.
print() simply prints a value, so we can see it. It does not return anything. A function that doesn’t include an explicit return statement will implicitly return None in Python. When the SCT for the exercise checks your code, None is being returned instead of lst.

P.S. For future posts, please review How do I format code in my posts?

I also got confused, not sure what’s wrong with the code I wrote:
The result showed function if not defined. Need some help here!

def delete_starting_events(lst):
  for num in lst:
    if lst[0] % 2 == 0: 
      lst = lst [:1]
    else: 
      return lst

Spelling error, perhaps?

Excellent samples.

I understand how the code executes, but please explain the concept of empty list as it computes during first print statement execution. Is an empty list actually implanted in the list? How/where is it apparent in the result?

Or is it simply implied?

Thanks in Advance.

Hi @jaxenjoliellc,

We started out with this:

trees = ["white oak",
         "tuliptree",
         "blue spruce",
         "tamarack",
         "paper birch",
         "honey locust",
         "basswood"]

The following statement replaces the slice specified to the left of the = operator with the contents of the list to the right of the operator:

trees[3:6] = []

Since the list to the right of the operator is empty, the effect is to remove the elements in the slice from the list. That result is reflected in the output of the print statement that follows the removal. What remains is this:

['white oak', 'tuliptree', 'blue spruce', 'basswood']

There’s no empty list that remains there. Rather, there is the effect of using a temporary empty list to remove a slice.

1 Like

Got it. The empty list’s “contents” replaces the “contents” or elements in the slice.
Thank you @appylpye.

1 Like

Aside

‘empty list’ and ‘contents’ are diametrically opposed since there cannot be one if there is the other.

  • an empty list cannot be accessed
  • a list may contain undefined elements
  • content of any sort means not empty

These are just things to have in mind. Not stressing any point, here.

1 Like

Just for fun.
If assigning an empty list to a list slice effectively removes the slice, what other values do the same?

trees = ["white oak",
         "tuliptree",
         "blue spruce",
         "tamarack",
         "paper birch",
         "honey locust",
         "basswood"]
         
trees[1:2] = [] #empty list
print(trees)
trees[1:2] = () #empty tuple
print(trees)
trees[1:2] = {} #empty dict
print(trees)
trees[1:2] = '' #empty string
print(trees)
trees[1:2] = zip([],[]) #empty zip obj
print(trees)
trees[1:2] = 'family tree' #what happens here?
print(trees)

Output:

[‘white oak’, ‘blue spruce’, ‘tamarack’, ‘paper birch’, ‘honey locust’, ‘basswood’]
[‘white oak’, ‘tamarack’, ‘paper birch’, ‘honey locust’, ‘basswood’]
[‘white oak’, ‘paper birch’, ‘honey locust’, ‘basswood’]
[‘white oak’, ‘honey locust’, ‘basswood’]
[‘white oak’, ‘basswood’]
[‘white oak’, ‘f’, ‘a’, ‘m’, ‘i’, ‘l’, ‘y’, ’ ', ‘t’, ‘r’, ‘e’, ‘e’]

1 Like

Okay so I was taking a look at appylpye’s example of how we can use slicing to “remove, add, and replace portions of a list.”

trees = [“white oak”,
“tuliptree”,
“blue spruce”,
“tamarack”,
“paper birch”,
“honey locust”,
“basswood”]

Anddd I’m very confused!

When you have something like trees[3:3] or trees[1:1], what slice of the list is being represented here?

If we look at how a slice works we see that the end is excluded.

>>> trees[3:3]
[]
>>> 

All we have is a start position that is excluded since it matches the end position. The result as we see is the empty list.

The number of items in the slice will be, end - start. Eg. 3 - 3 = 0.

>>> trees[3:4]
tamarack
>>>

Same as, trees[3].

All right, so it’s an empty list. That makes sense! But in the example, @appylpye sets trees[1:1] =[“pawpaw”, “river birch”]. Then when you print(trees) the list comes out to [‘white oak’, ‘pawpaw’, ‘river birch’, ‘tuliptree’, ‘blue spruce’, ‘basswood’]. But if trees[1:1] is actually an empty list, then how do we know where the set of [“pawpaw”, “river birch”] is supposed to go? Why is it plugged into where index 1 starts rather than, say, at the end of the list?

I hope that question makes sense.