Python Language List

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

Internal

Overview

A list is a mutable sequence type that contains zero or more elements and whose elements can be of the same type or different types. The elements of a list are ordered. A list can be changed in-place, new elements can be added to it, and existing elements can be overwritten. Unlike a set, a list can contain the same element multiple times, a list element does not need to be unique in the list.

List type()

The function type() applied to a list returns:

<class 'list'>

To check whether an instance is a list:

i = ...
if type(i) is list:
  ...

For list subclasses:

i = ...
if isinstance(i, list):
  ...

List Comparison

Two different list instances that contain the same values in the same order are considered equal:

l = ['a', 'b', 'c']
l2 = ['a', 'b', 'c']
assert l == l2 # passes

Create a List

A list can be created with the [] syntax, with the list() functions and with list comprehensions.

Create a List with []

A list can be created specifying the list elements, separate them by comma and enclose them in square brackets.

empty_list = []
some_list = ['A', 'B', 'C']
some_other_list = ['A', 2, 3.0, ['B', 4]]

Create a List or Convert other Data Type to a List with list()

An empty list can be created with the list() function:

empty_list = list()

The list() function converts the data types to lists. list() applied to a list creates a copy of that list. The data types that can be converted are:

String to List

s = 'abc'
l = list(s)
assert l == ['a', 'b', 'c']

Tuple to List

t = ('a', 'b', 'c')
l = list(t)
assert l == ['a' ,'b', 'c']

Pass a Generator Expression

gen = range(10)
l = list(gen) # creates a [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] list

Also see:

Generators

Create a List of Specific Size

To create a list of specific size, so arbitrary elements can be set in place at a specific index with list[offset] = ..., use this syntax:

l = [None] * 5
l[2] = 'A' # [None, None, 2, None, None]

Create a Copy of a List

l = ['a', 'b', 'c']
l2 = l.copy()

l[0] = 'x'
assert 'x' == l[0]
assert 'a' == l2[0]

Create a List and Initialize it with a Function Return Values

import random
data = [random.randint(0, 10) for i in range(10)]

Access a List

Test for Empty List

Test for Existence of an Element in List with the in Keyword

A Python idiomatic way to test whether a value is in the list is using in:

l = ['a', 'b', 'c', 'b', 'd']
if 'a' in l:
  print("'a' found")
else:
  print("'a' NOT found")
if 'x' in l:
  print("'x' found")
else:
  print("'x' NOT found")

not in can be used to assess non-existence.

The offset of an element with a certain value can be returned by calling index().

l = ['a', 'b', 'c', 'b', 'd']
assert 1 == l.index('b')

If the element exists in list, the index() returns the offset of the first value in the list, in case the value exists more than once. If the value does not exist, the method will throw ValueError: '...' is not in list.

Access an Element in List

The element of a list can be accessed using the [<offset>] syntax. The offset is zero-based, and it can be positive or negative.

l = ['a', 'b', 'c']
print(l[0])

Negative indices count backward from the end, the last element is identified with a -1 index. Think about it as index = len(list) - negative-index.

l = ['a', 'b', 'c']
assert 'c' == l[-1]
assert 'b' == l[-2]

⚠️ In case of both positive and negative indices, the offset should be valid. If the indices fall outside of the list's bounds, IndexError: list index out of range exception is thrown.

Length of a List

The number of elements is given by the len() function:

l = ['a', 'b', 'c']
assert 3 == len(l)

Count Occurrences of a Value with count()

l = ['a', 'b', 'c', 'a']
assert 2 == l.count('a')

Iterate over a List

Python Language | Loops and Iterations

Slices

Slicing Lists and Tupes in Python

Modify a List

Modify Individual Elements

Modify an Element Identified by [offset]

An element of a list can be modified using the [<offset>] syntax. The offset is zero-based, and it can be positive or negative, as described in Access an Element in List.

l = ['a', 'b', 'c']
l[1] = 'd'
print(l[1])

Append an Element

The traditional way of adding items to a list is to append() them one by one to the end of the list. The invocation modifies the list in-place and increases the length of the list.

l = [1, 2, 3]
l.append(10)
print(l) # will display [1, 2, 3, 10]

append() adds the argument to the end of the list, as one element, even if it's another list. To extend the list by adding the elements individually, use extend() or +=.

⚠️ Do not use append() to append the content of another list. append() works only for individual elements, if used with a list, it appends the list itself, not its elements. Use extent() instead.

Append a List

A list can be extended with another list with the extend() function:

l1 = [1, 2, 3]
l2 = [10, 20]
l1.extend(l2)
print(l1) # will display [1, 2, 3, 10, 20]

Note that the argument of the extend() function may not be None. If that happens, we get:

TypeError: 'NoneType' object is not iterable

The extend() function is equivalent with the += operator:

l1 = [1, 2, 3]
l2 = [10, 20]
l1 += l2
print(l1) # will display [1, 2, 3, 10, 20]

Insert an Element in the Middle of a List

insert(offset, element) inserts an element in the middle of the list. ⚠️ insert() will increase the list length. For a 0 offset, the function will insert the element at the beginning of the list. If the offset falls outside the list boundaries, it will add at the end of the list without throwing an exception. When an item is deleted from a list, the items that follow it shift left to take its position and the length of the list decreases by one.

l1 = [1, 2, 3]
l1.insert(1, 10)
print(l1) # will display [1, 10, 2, 3]
l1.insert(1000, 20)
print(l1) # will display [1, 10, 2, 3, 20]
l1.insert(-1000, 40)
print(l1) # will display [40, 1, 10, 2, 3, 20]

insert() is computationally expensive compared with append() because reference to subsequent elements have to be shifted internally to make room for the new element. If you need to insert elements at both the beginning and end of a sequence, you may wish to explore collections.deque(), a double-ended queue, which is optimized for this purpose, available in Python Standard library.

Delete an Element by Offset

To delete an element by offset use del list_name[<offset>]. The offset can be positive or negative. Using -1 deletes the last element from the list.

l = [1, 2, 3]
del l[0] # the list is now [2, 3]
del l[-1] # the list is now [2]

Remove (Delete) an Element by its Value

remove() deletes an element from a list based on its value and compacts the list.

l = ['a', 'b', 'c']
l.remove('b')
assert l == ['a', 'c']

If more than one elements have the same value, only the first one is removed:

l = ['a', 'b', 'c', 'b', 'd']
l.remove('b')
assert l == ['a', 'c', 'b', 'd']

If the element is not present, the method raises ValueError:

l = ['a', 'b', 'c']
try:
  l.remove('d')
except ValueError:
  pass
assert l == ['a', 'b', 'c']

Return and Remove an Element with pop()

An element identified by offset can be returned and deleted from the list with pop(). The operation is inverse to insert(). Positives and negative offsets work.

l = [1, 2, 3]
print(l.pop(1)) # displays 2
print(l) # displays [1, 3]

pop() without argument implies -1: the last element of the list will be returned and deleted.

Reverse a List In-place

A list can be reversed in-place with reverse().

l = [1, 2, 3]
l.reverse()
print(l) # displays [3, 2, 1]

⚠️ reverse() returns None, not a list! The reversal is done in place. The fact that the method does not return anything is counterintuitive, it cannot use as part of a flow syntax.

Note that the list can be iterated in reverse order with the built-in reversed().

Remove an Element while Iterating

Evaluate the list by index, going backwards. Note that if we're storing the processed and deleted elements into secondary storage, at the end it must be revered to preserve the original order.

l = ['a', 'b', 'c', 'a', 'd']

for i in range(len(l)-1, -1, -1):
    if l[i] == 'a':
        del l[i]

assert l == ['b', 'c', 'd']

Delete All Elements

Copy a List

lst = [2, 3, 5]
lst_copy = lst.copy()

Printing Lists

If print() is called on list that contain custom class instances, __str()__ does not seem to be called on instances.

Multi-Dimensional Lists

m = [[1, 2, 3],[4, 5, 6],[7, 8, 9]]
print(m[2][1]) # prints 8

List Processing

split(), join()

A string can be converted into a list containing tokens delimited by separators in the string using the split() function. split() is a string function. The elements of the newly created list are strings.

s='a b c'
l = s.split(' ') # l is ['a', 'b', 'c']

If two separators occur in succession in the string, the list contains an empty string:

s='a,,b,c'
l = s.split(',') # l is ['a', '', 'b', 'c']

The string can then be put back together with join(). Note that join() is a string method, not a list method, so it should be applied to the separator string. The elements of the list must be strings, otherwise an exception is thrown: TypeError: sequence item 0: expected str instance, int found.

l = ['a', 'b', 'c', 'd']
s = ', '.join(l)
print(s) # will display a, b, c, d

join() being a string method, can be used with other iterable types like tuple or set.

Sort

There are two methods that can be used to sort lists: list.sort() and sorted(). The sort() sorts the list in-place, while sorted() creates a copy of the list and leaves the argument unmodified:

l = ['c', 'b', 'a']
l.sort()
assert ['a', 'b', 'c'] == l

The default sort order is ascending, but if the sort(reverse=True) is invoked with reverse=True, the order will be switched to descending.

l = ['c', 'b', 'a']
l2 = sorted(l)
assert ['c', 'b', 'a'] == l
assert ['a', 'b', 'c'] == l2

For a discussion on how elements are compared, see:

sorted() | Comparing Elements

Iterate over a List in Reversed Order

TO PROCESS PyOOP "Reversed"

reversed() built-in can be used to iterate over a list in reversed order:

l = ['a', 'b', 'c']
for i in reversed(l):
  ...

The list is left unchanged. Note that a list can be revered in-place with reverse()

Access the Index and the Element at the Same Time with enumerate()

TO PROCESS PyOOP "Enumerate"

The enumerate() built-in function gives access to the elements of the list and their index at the same time.

l = ['a', 'b', 'c']
for i, e in enumerate(l):
    print(f'index: {i}, element: {e}')

List Concatenation with +

Two lists can be concatenated with the + operator:

['a', 'b', 'c'] + ['x', 'y']

Not that list concatenation by addition is expensive since a new list must be created and the objects copied over. Using extend() to append elements to an existing list is usually preferable.

Subclass a List

class MockList(list):
    # noinspection PyUnusedLocal
    def __init__(self, *args, **kwargs):
        super().__init__()
        milestone = kwargs.get('milestone')
        if milestone is None:
            self._storage = test_issues.copy()
        elif milestone == '*':
            self._storage = [get_mock_issue(1, 'issue 1')]
        elif milestone == 'none':
            self._storage = [get_mock_issue(2, 'issue 2')]
        elif isinstance(milestone, Mock):
            self._storage = [get_mock_issue(3, 'issue 3')]
        else:
            raise ValueError(f'unexpected milestone: {milestone}')

    def __str__(self):
        return str(self._storage)

    def __len__(self, *args, **kwargs):
        return len(self._storage)

    def __getitem__(self, y):
        return self._storage.__getitem__(y)

    def __iter__(self, *args, **kwargs):
        return self._storage.__iter__()