DEV Community

Cover image for Python Lists
Sundeep
Sundeep

Posted on • Originally published at learnbyexample.github.io

Python Lists

List is a container data type, similar to tuple but mutable. Lists are typically used to store and manipulate ordered collection of values.

Tuple and Sequence operations chapter is a prerequisite for this one.

Initialization and Slicing

Lists are declared as a comma separated values within [] square brackets. Unlike tuple there's no ambiguity in using [] characters, so there's no special requirement of trailing comma for a single element list object. You can use a trailing comma if you wish, which is helpful to easily change a list declared across multiple lines.

# 1D example
>>> vowels = ['a', 'e', 'i', 'o', 'u']
>>> vowels[0]
'a'
# same as vowels[4] since len(vowels) - 1 = 4
>>> vowels[-1]
'u'

# 2D example
>>> student = ['learnbyexample', 2021, ['Linux', 'Vim', 'Python']]
>>> student[1]
2021
>>> student[2]
['Linux', 'Vim', 'Python']
>>> student[2][-1]
'Python'
Enter fullscreen mode Exit fullscreen mode

Since list is a mutable data type, you can modify the object after initialization. You can either change a single element or multiple elements using slicing notation.

>>> nums = [1, 4, 6, 22, 3, 5]

>>> nums[0] = 100
>>> nums
[100, 4, 6, 22, 3, 5]

>>> nums[-3:] = [-1, -2, -3]
>>> nums
[100, 4, 6, -1, -2, -3]

# list will automatically shrink/expand as needed
>>> nums[1:4] = [2000]
>>> nums
[100, 2000, -2, -3]
>>> nums[1:2] = [3.14, 4.13, 6.78]
>>> nums
[100, 3.14, 4.13, 6.78, -2, -3]
Enter fullscreen mode Exit fullscreen mode

List methods and operations

This section will discuss some of the list methods and operations. See docs.python: list methods for documentation. As mentioned earlier, you can use dir(list) to view the available methods of an object.

Use the append() method to add a single element to the end of a list object. If you need to append multiple items, you can pass an iterable to the extend() method. As an exercise, check what happens if you pass an iterable to the append() method and a non-iterable value to the extend() method. What happens if you pass multiple values to both these methods?

>>> books = []
>>> books.append('Cradle')
>>> books.append('Mistborn')
>>> books
['Cradle', 'Mistborn']

>>> items = [3, 'apple', 100.23]
>>> items.extend([4, 'mango'])
>>> items
[3, 'apple', 100.23, 4, 'mango']
>>> items.extend((-1, -2))
>>> items.extend(range(3))
>>> items.extend('hi')
>>> items
[3, 'apple', 100.23, 4, 'mango', -1, -2, 0, 1, 2, 'h', 'i']
Enter fullscreen mode Exit fullscreen mode

The count() method will give the number of times a value is present.

>>> nums = [1, 4, 6, 22, 3, 5, 2, 1, 51, 3, 1]
>>> nums.count(3)
2
>>> nums.count(31)
0
Enter fullscreen mode Exit fullscreen mode

The index() method will give the index of the first occurrence of a value. As seen with tuple, this method will raise ValueError if the value isn't present.

>>> nums = [1, 4, 6, 22, 3, 5, 2, 1, 51, 3, 1]

>>> nums.index(3)
4
Enter fullscreen mode Exit fullscreen mode

The pop() method removes the last element of a list by default. You can pass an index to delete that specific item and the list will be automatically re-arranged. Return value is the element being deleted.

>>> primes = [2, 3, 5, 7, 11]
>>> last = primes.pop()
>>> last
11
>>> primes
[2, 3, 5, 7]
>>> primes.pop(2)
5
>>> primes
[2, 3, 7]

>>> student = ['learnbyexample', 2021, ['Linux', 'Vim', 'Python']]
>>> student.pop(1)
2021
>>> student[-1].pop(1)
'Vim'
>>> student
['learnbyexample', ['Linux', 'Python']]
>>> student.pop()
['Linux', 'Python']
>>> student
['learnbyexample']
Enter fullscreen mode Exit fullscreen mode

To remove multiple elements using slicing notation, use the del statement. Unlike the pop() method, there is no return value.

>>> nums = [1.2, -0.2, 0, 2, 4, 23]
>>> del nums[0]
>>> nums
[-0.2, 0, 2, 4, 23]
>>> del nums[2:4]
>>> nums
[-0.2, 0, 23]

>>> nums_2d = [[1, 3, 2, 10], [1.2, -0.2, 0, 2], [100, 200]]
>>> del nums_2d[0][1:3]
>>> del nums_2d[1]
>>> nums_2d
[[1, 10], [100, 200]]
Enter fullscreen mode Exit fullscreen mode

The pop() method deletes an element based on its index. Use the remove() method to delete an element based on its value. You'll get ValueError if the value isn't found.

>>> even_numbers = [2, 4, 6, 8, 10]
>>> even_numbers.remove(8)
>>> even_numbers
[2, 4, 6, 10]
Enter fullscreen mode Exit fullscreen mode

The clear() method removes all the elements. You might wonder why not just assign an empty list? If you have observed closely, all of the methods seen so far modified the list object in-place. This is useful if you are passing a list object to a function and expect the function to modify the object itself instead of returning a new object. More on that later.

>>> nums = [1.2, -0.2, 0, 2, 4, 23]
>>> nums.clear()
>>> nums
[]
Enter fullscreen mode Exit fullscreen mode

You've already seen how to add element(s) at the end of a list using append() and extend() methods. The insert() method is the opposite of pop() method. You can provide a value to be inserted at the given index. As an exercise, check what happens if you pass a list value. Also, what happens if you pass more than one value?

>>> books = ['Sourdough', 'Sherlock Holmes', 'To Kill a Mocking Bird']
>>> books.insert(2, 'The Martian')
>>> books
['Sourdough', 'Sherlock Holmes', 'The Martian', 'To Kill a Mocking Bird']
Enter fullscreen mode Exit fullscreen mode

The reverse() method reverses a list in-place. If you use slicing notation, you'll get a new object.

>>> primes = [2, 3, 5, 7, 11]
>>> primes.reverse()
>>> primes
[11, 7, 5, 3, 2]

>>> primes[::-1]
[2, 3, 5, 7, 11]
>>> primes
[11, 7, 5, 3, 2]
Enter fullscreen mode Exit fullscreen mode

Here's some examples with comparison operators. Quoting from documentation:

For two collections to compare equal, they must be of the same type, have the same length, and each pair of corresponding elements must compare equal (for example, [1,2] == (1,2) is false because the type is not the same).

Collections that support order comparison are ordered the same as their first unequal elements (for example, [1,2,x] <= [1,2,y] has the same value as x <= y). If a corresponding element does not exist, the shorter collection is ordered first (for example, [1,2] < [1,2,3] is true).

>>> primes = [2, 3, 5, 7, 11]
>>> nums = [2, 3, 5, 11, 7]
>>> primes == nums
False
>>> primes == [2, 3, 5, 7, 11]
True

>>> [1, 1000] < [2, 3]
True
>>> [1000, 2] < [1, 2, 3]
False

>>> ['a', 'z'] > ['a', 'x']
True
>>> [1, 2, 3] > [10, 2]
False
>>> [1, 2, 3] > [1, 2]
True
Enter fullscreen mode Exit fullscreen mode

Sorting and company

The sort() method will order the list object in-place. The sorted() built-in function provides the same functionality for iterable types and returns an ordered list.

>>> nums = [1, 5.3, 321, 0, 1, 2]

# ascending order
>>> nums.sort()
>>> nums
[0, 1, 1, 2, 5.3, 321]

# descending order
>>> nums.sort(reverse=True)
>>> nums
[321, 5.3, 2, 1, 1, 0]

>>> sorted('fuliginous')
['f', 'g', 'i', 'i', 'l', 'n', 'o', 's', 'u', 'u']
Enter fullscreen mode Exit fullscreen mode

The key argument accepts the name of a built-in/user-defined function (i.e. function object) for custom sorting. If two elements are deemed equal based on the result of the function, the original order will be maintained (known as stable sorting). Here's some examples:

# based on the absolute value of an element
# note that the input order is maintained for all three values of "4"
>>> sorted([-1, -4, 309, 4.0, 34, 0.2, 4], key=abs)
[0.2, -1, -4, 4.0, 4, 34, 309]

# based on the length of an element
>>> words = ('morello', 'irk', 'fuliginous', 'crusado', 'seam')
>>> sorted(words, key=len, reverse=True)
['fuliginous', 'morello', 'crusado', 'seam', 'irk']
Enter fullscreen mode Exit fullscreen mode

If the custom user-defined function required is just a single expression, you can create anonymous functions with lambda expressions instead of a full-fledged function. As an exercise, read docs.python: Sorting and implement the below examples using operator module instead of lambda expressions.

# based on second element of each item
>>> items = [('car', 20), ('jeep', 3), ('cycle', 5)]
>>> sorted(items, key=lambda e: e[1], reverse=True)
[('car', 20), ('cycle', 5), ('jeep', 3)]

# based on number of words, assuming space as the word separator
>>> dishes = ('Poha', 'Aloo tikki', 'Baati', 'Khichdi', 'Makki roti')
>>> sorted(dishes, key=lambda s: s.count(' '), reverse=True)
['Aloo tikki', 'Makki roti', 'Poha', 'Baati', 'Khichdi']
Enter fullscreen mode Exit fullscreen mode

You can use sequence types like list or tuple to specify multiple sorting conditions. Make sure to read the sequence comparison examples from previous section before trying to understand the following examples.

>>> dishes = ('Poha', 'Aloo tikki', 'Baati', 'Khichdi', 'Makki roti')

# word-count and dish-names, both descending order
>>> sorted(dishes, key=lambda s: (s.count(' '), s), reverse=True)
['Makki roti', 'Aloo tikki', 'Poha', 'Khichdi', 'Baati']

# word-count descending order, dish-names ascending order
# the main trick is to negate the numerical value
>>> sorted(dishes, key=lambda s: (-s.count(' '), s))
['Aloo tikki', 'Makki roti', 'Baati', 'Khichdi', 'Poha']
Enter fullscreen mode Exit fullscreen mode

As an exercise, given nums = [1, 4, 5, 2, 51, 3, 6, 22], determine and implement the sorting condition based on the required output shown below:

  • [4, 2, 6, 22, 1, 5, 51, 3]
  • [2, 4, 6, 22, 1, 3, 5, 51]
  • [22, 6, 4, 2, 51, 5, 3, 1]

Here's some examples with min() and max() functions.

>>> nums = [321, 0.5, 899.232, 5.3, 2, 1, -1]
>>> min(nums)
-1
>>> max(nums)
899.232
>>> min(nums, key=abs)
0.5
Enter fullscreen mode Exit fullscreen mode

Random items

You have already seen a few examples with random module in earlier chapters. This section will show a few examples with methods that act on sequence data types.

First up, getting a random element from a non-empty sequence using the choice() method.

>>> import random

>>> random.choice([4, 5, 2, 76])
76
>>> random.choice('hello')
'e'
Enter fullscreen mode Exit fullscreen mode

The shuffle() method randomizes the elements of a list in-place.

>>> items = ['car', 20, 3, 'jeep', -3.14, 'hi']

>>> random.shuffle(items)
>>> items
['car', 3, -3.14, 'jeep', 'hi', 20]
Enter fullscreen mode Exit fullscreen mode

Use the sample() method to get a list of specified number of random elements. As an exercise, see what happens if you pass a slice size greater than the number of elements present in the input sequence.

>>> random.sample((4, 5, 2, 76), k=3)
[4, 76, 2]

>>> random.sample(range(1000), k=5)
[490, 26, 9, 745, 919]
Enter fullscreen mode Exit fullscreen mode

Map, Filter and Reduce

Many operations on container objects can be defined in terms of these three concepts. For example, if you want to sum the square of all even numbers:

  • separating out even numbers is Filter (i.e. only elements that satisfy a condition are retained)
  • square of such numbers is Map (i.e. each element is transformed by a mapping function)
  • final sum is Reduce (i.e. you get one value out of multiple values)

One or more of these operations may be absent depending on the problem statement. A function for the first of these steps could look like:

>>> def get_evens(iter):
...     op = []
...     for n in iter:
...         if n % 2 == 0:
...             op.append(n)
...     return op
... 
>>> get_evens([100, 53, 32, 0, 11, 5, 2])
[100, 32, 0, 2]
Enter fullscreen mode Exit fullscreen mode

Function after the second step could be:

>>> def sqr_evens(iter):
...     op = []
...     for n in iter:
...         if n % 2 == 0:
...             op.append(n * n)
...     return op
... 
>>> sqr_evens([100, 53, 32, 0, 11, 5, 2])
[10000, 1024, 0, 4]
Enter fullscreen mode Exit fullscreen mode

And finally, the function after the third step could be:

>>> def sum_sqr_evens(iter):
...     total = 0
...     for n in iter:
...         if n % 2 == 0:
...             total += n * n
...     return total
... 
>>> sum_sqr_evens([100, 53, 32, 0, 11, 5, 2])
11028
Enter fullscreen mode Exit fullscreen mode

Here's some examples with sum(), all() and any() built-in reduce functions.

>>> sum([321, 0.5, 899.232, 5.3, 2, 1, -1])
1228.032

>>> conditions = [True, False, True]
>>> all(conditions)
False
>>> any(conditions)
True
>>> conditions[1] = True
>>> all(conditions)
True

>>> nums = [321, 1, 1, 0, 5.3, 2]
>>> all(nums)
False
>>> any(nums)
True
Enter fullscreen mode Exit fullscreen mode

Python also provides map(), filter() and functools.reduce() for such problems. But, see Comprehensions and Generator expressions chapter before deciding to use them.

Exercises

  • Write a function that returns the product of a sequence of numbers. Empty sequence or sequence containing non-numerical values should raise TypeError.
    • product([-4, 2.3e12, 77.23, 982, 0b101]) should give -3.48863356e+18
    • product(range(2, 6)) should give 120
    • product(()) and product(['a', 'b']) should raise TypeError
  • Write a function that removes dunder names from dir() output.

    >>> remove_dunder(list)
    ['append', 'clear', 'copy', 'count', 'extend', 'index',
     'insert', 'pop', 'remove', 'reverse', 'sort']
    >>> remove_dunder(tuple)
    ['count', 'index']
    

Discussion (0)