Список (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 []
равно[]