Список (list)

Списки в Python - упорядоченные изменяемые коллекции объектов произвольных типов (почти как массив в других языках, но типы элементов могут отличаться).

Все методы неизменяемых и изменяемых последовательностей + метод sort.

Все методы списка (в ipython или тетради): dir(list) или help(list)

Help по 1 методу (например, append): list.append?

Создание списка

Пустой список:

a1 = []
a2 = list()

Список с данными:

a = ['apple', 'banana', 'wildberry']
b = [12, 34, -5, 16]
c = [12, 'apple', [3.14, 9.81], 'orange' ]
d = list('hello')

Индексы

Индексы начинаются с 0.

-1 - последний элемент.

     +---+---+---+---+---+---+
  x  | P | y | t | h | o | n |        значение (число), value
     +---+---+---+---+---+---+
  i    0   1   2   3   4   5   6=len  номер (index)
      -6  -5  -4  -3  -2  -1
a = list('python')
for i, x in enumerate(a):
    print(i, x)

print('length =', len(a))

# Output:
# Output:
# 0 p
# 1 y
# 2 t
# 3 h
# 4 o
# 5 n
# length = 6

Срез (slice) - часть списка

Можно обратиться к части массива от номера i (включая) до номера j (не включая). Математики запишут это как [i, j) Программисты запишут срез (slice) a[i:j] и сделают новый список.

>>> a = [3, 5, -2, 10, 8, 1, 17]
>>> a[3]
10
>>> a[1:4]
[5, -2, 10]
>>> a[-5:-2]
[-2, 10, 8]

У срезов есть (как у range) третий аргумент - шаг.

a[i:j:k] - взять срез списка a от i (включая) до j (НЕ включая) с шагом (прибавить) k.

>>> a = [3, 5, -2, 10, 8, 1, 17]
>>> a[1:6:2]
[5, 10, 1]
>>> a[-2:-5:-1]
[1, 8, 10]

Можно не писать номер начала или номер конца среза. Тогда это будет "начало" и "конец" списка.

>>> a = [3, 5, -2, 10, 8, 1, 17]
>>> a[:4]
[3, 5, -2, 10]
>>> a[3:]
[10, 8, 1, 17]
>>> a[:4:2]
[3, -2]
>>> a[3::2]
[10, 1]
>>> a[::2]
[3, -2, 8, 17]
>>> a[::-1]
[17, 1, 8, 10, -2, 5, 3]

Ссылки

В списке хранятся только ссылки на объекты.

m = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
print(m)       # [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
m[0][0] = 10
print(m)       # [[10, 2, 3], [1, 2, 3], [1, 2, 3]]

a = [1, 2, 3]
m = [a, a, a]  # m содержит 3 ссылки на один и тот же список a
print(m)       # [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
m[0][0] = 10
print(m)       # [[10, 2, 3], [10, 2, 3], [10, 2, 3]]

Как изменить код последнего примера, чтобы для создания списка использовался все тот же список а, но изменение элемента m[0][0]=10 не изменяло другие элементы матрицы?

Копирование списка

Копировать список a можно:

  • list(a)
  • a[:]
  • a.copy()

Shell copy, deep copy

  • shell copy - копируем ссылки
  • deep copy - идем по ссылкам рекурсивно и копируем данные (проблема с циклическими ссылками)
>>> a = [1, 2, 3]
>>>
>>> m1 = [a.copy(), a.copy(), a.copy()]
>>> m1
[[1, 2, 3], [1, 2, 3], [1, 2, 3]]
>>> m1[0][0] = 10
>>> m1
[[10, 2, 3], [1, 2, 3], [1, 2, 3]]
>>> m2 = m1.copy()                  # m2 содержит копии ссылок из m1, а не копии списков
>>> m2
[[10, 2, 3], [1, 2, 3], [1, 2, 3]]
>>> m2[1][0] = 77                   # меняем список из m2
>>> m2
[[10, 2, 3], [77, 2, 3], [1, 2, 3]]
>>> m1                              # видим изменения в m1
[[10, 2, 3], [77, 2, 3], [1, 2, 3]]
>>> import copy
>>> m3 = copy.deepcopy(m1)          # копируем данные
>>> m3
[[10, 2, 3], [77, 2, 3], [1, 2, 3]]
>>> m3[2][0] = 666                  # меняем список в m3
>>> m3
[[10, 2, 3], [77, 2, 3], [666, 2, 3]]
>>> m2                              # изменения не затронули m2
[[10, 2, 3], [77, 2, 3], [1, 2, 3]]
>>> m1                              # изменения не затронули m1
[[10, 2, 3], [77, 2, 3], [1, 2, 3]]

Изменяем список

>>> [1, 2, 3] + [4, 5]
[1, 2, 3, 4, 5]
>>> [1, 2] * 3
[1, 2, 1, 2, 1, 2]

Оба примера сделают новый список.

Разница между append и extend

a = [1, 2, 3]
a.append([4, 5])
print(a)           # [1, 2, 3, [4, 5]] в списке 4 элемента, последний элемент - список [4, 5]

b = [1, 2, 3]
b.extend([4, 5])
print(b)           # [1, 2, 3, 4, 5] в списке 5 элементов

b2 = [1, 2, 3]
b2 += [4, 5]
print(b)           # [1, 2, 3, 4, 5] в списке 5 элементов

Все эти методы изменяют уже существующий список, а не создают новый.

Разница между append и +

Конкатенация (+) создает новый объект, а метод append нет. Поэтому append работает быстрее.

a.append(x) в конец работает как a[len(a):] = [x]

a[:0] = [x] - добавить в начало списка. Работают так же быстро, как append.

Изменение списка с помощью slice (удаление)

  • del элемент
  • del часть списка
  • del переменная
a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0]
print(a)        # [1, 66.25, 333, 333, 1234.5]

del a[2:4]
print(a)        # [1, 66.25, 1234.5]

del a[:]        # удалим ВСЕ ЭЛЕМЕНТЫ из списка а, (можно было так: a.clear() или a = [])
print(a)        # []

Можно удалить переменную:

a = [-1, 1, 66.25, 333, 333, 1234.5]
del a
print(a)    # ошибка! переменной а больше нет!

Изменение списка с помощью slice (удаление и вставка)

Удаление всего куска слева и вставка в это место куска справа:

>>> a = [1, 2, 3, 4, 5]
>>> a[2:4] = [10, 11, 12, 13]
>>> a
[1, 2, 10, 11, 12, 13, 5]

Заметьте, размеры удаленной и вставляемой части не обязаны совпадать.

Проверка принадлежности и сравнение

  • is - сравнение ссылок на объекты
  • in - элемент принадлежит списку
  • ==, !=, <, >, <=, >= - поэлементное сравнение списков (возможно рекурсивное). Подробнее в сортировках
>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a is b
False
>>> a == b
True
>>> 3 in a
True
>>> 88 in a
False

Проверить, что список пустой

bool(a) возвращает False, если a пустой список. Иначе он возвращает True.

a = []
if a:
    print('NON-empty list')
else:
    print('Empty list')
if not a:   # поверка, что лист ПУСТОЙ

split и join

См. строки

List comprehensions

Общий вид как сделать список:

[выражение for переменная in последовательность]
или
[выражение for переменная in последовательность if условие]

Как сделать список, написав меньше кода?

Привычный вариант:

a = []
for x in range(5):
     a.append(x**2)

Если функция достаточно сложная, можно ее написать отдельно:

def sqr(x):
    return x**2
a = list(map(sqr, range(5)))

А если простая, то записать через lambda:

a = list(map(lambda x: x**2, range(10)))

Или, как большинство программистов на питоне, использовать list comprehensions

a = [x**2 for x in range(5)]       # [0, 1, 4, 9, 16]

Примеры:

>>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

то же самое, что:

>>> combs = []
>>> for x in [1,2,3]:
...     for y in [3,1,4]:
...         if x != y:
...             combs.append((x, y))
...
>>> combs
[(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

Обратите внимание на порядок for и if.

>>> vec = [-4, -2, 0, 2, 4]
>>> [x*2 for x in vec]                # create a new list with the values doubled
[-8, -4, 0, 4, 8]

>>> [x for x in vec if x >= 0]        # filter the list to exclude negative numbers
[0, 2, 4]

>>> [abs(x) for x in vec]             # apply a function to all the elements
[4, 2, 0, 2, 4]
                                      # call a method on each element
>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit]
['banana', 'loganberry', 'passion fruit']

>>> [(x, x**2) for x in range(6)]     # create a list of 2-tuples like (number, square)
[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
>>> # the tuple must be parenthesized, otherwise an error is raised
>>> [x, x**2 for x in range(6)]
  File "<stdin>", line 1, in <module>
    [x, x**2 for x in range(6)]
               ^
SyntaxError: invalid syntax

>>> vec = [[1,2,3], [4,5,6], [7,8,9]]
>>> [num for elem in vec for num in elem]   # flatten a list using a listcomp with two 'for'
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Nested List Comprehensions (генерация вложенных списков)

Дана матрица

>>> matrix = [
...     [1, 2, 3, 4],
...     [5, 6, 7, 8],
...     [9, 10, 11, 12],
... ]

Надо ее транспонировать.

t = [[row[i] for row in m] for i in range(4)]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

чуть подробнее:

transposed = []
for i in range(4):
    transposed.append([row[i] for row in matrix])

еще подробнее:

transposed = []
for i in range(4):
    # the following 3 lines implement the nested listcomp
    transposed_row = []
    for row in matrix:
        transposed_row.append(row[i])
    transposed.append(transposed_row)

или через функцию zip()

list(zip(*matrix))

Не пилите сук, на котором сидите

Не изменяйте последовательность, которую перебираете в цикле.

Как напечатать список

Дан список a = [3, 5, -2, 10]. Надо его напечатать.

Как пишут в программе:

print(a)        # [3, 5, -2, 10]

В столбик:

for x in a:
    print(x)
# Output:
# 3
# 5
# -2
# 10

В строку, используем аргумент end функции print:

for x in a:
    print(x, end=' ')  # печатаем через пробел
print()                # в конце 1 раз - новая строка

В строку, используя оператор * (развернуть список в его элементы):

a = [3, 5, -1, 10]
print(a)              # [3, 5, -1, 10]
print(*a)             # 3 5 -2 10

В строку через разделитель, используя join:

a = ['abc', 'qw', 'xyz']
s = ', '.join(a)            # abc, qw, xyz

В этом случае все элементы списка должны быть строками (требует join). Если это не так, сделаем их str.

a = [1, 3.4, 'abc', [12, 34]]
s = '-'.join(map(str, a))       # 1-3.4-abc-[12, 34]

Изменяемый объект в виде значения по умолчанию

Мы хотим в функцию передать список. Если список не задали, создать пустой список.

def func(x = 0, a = []):
    a.append(x)
    print(a)

Не пишите изменяемые объекты в значения по умолчанию.

Почему x = 0 - хорошо, a = [] - плохо?

Потому что эти объекты создаются 1 раз за все время работы программы перед первым вызовом функции.

(Для знающих языки С, С++ или Java - это static объекты).

Каждый новый вызов функции может изменить этот объект.

>>> def foo(x = 0, a = []):
...     a.append(x)
...     print(a)
...
>>> foo(3, [5, 7, 11])  # ок, пока не используем значение по умолчанию
[5, 7, 11, 3]
>>> foo(-4, [1, 2])
[1, 2, -4]

>>> foo(1)              # в список по умолчанию добавили 1 - ок
[1]
>>> foo(5)              # в ТОТ ЖЕ список по умолчанию (он уже [1], а не []), добавили 5
[1, 5]

Что делать? Как исправить?

Значениями по умолчанию делайте только неизменяемые объекты.

>>> def foo2(x = 0, a = None):
...     b = a or []
...     b.append(x)
...     print(b)
...
>>> foo2(1)
[1]
>>> foo2(5)
[5]

None - неизменяемый объект.

a or [] работает так:

  • если а - непустой список, то он в выражении будет True и выражение с or дальше вычисляться не будет, значение a or [] равно a.
  • если a это None, то None в логическом выражении будет False и выражение будет выполняться дальше, значение a or [] равно []

results matching ""

    No results matching ""