Списки в python
Списки особенно удобны для хранения в них объектов в определенном порядке, особенно если порядок или содержимое нужно будет изменить. Список изменяем, поэтому можно добавлять новые элементы, перезаписывать существующие или удалять. Одно и то же значение в списке может встречаться много раз
Синтаксис
a = [2, 4, 6]
# print(b) выведет [2, 4, 6]
Как в случае других переменных, иногда нужно задать пустое значение списку (как 0 для чисел или ” для строк). Делается так:
a = []
В списке ЛУЧШЕ хранить один тип данных, а не кидать туда всё подряд по типу
[3, 'test', 0, 'asd', False]
. Так нельзя, ибо доступ к отдельным элементам списка затрудняется.
Элементы списка (a[0]
, a[1]
и a[2]
) - это, по сути, имена, указывающие, in this case, на int объекты со значениями 2, 4, 6.
.append()
и .extend()
Чтобы добавить в конец списка элемент - нужно:
b.append(10)
# или
b.append('test')
# или
a = input()
b.append(a)
Если нужно добавить несколько элементов, то есть список, то нужно воспользоваться list.extend()
, потому что list.append()
принимает только один аргумент.
a = [4, 5, 6]
a.extend([1, 2, 3])
print(a) # [4, 5, 6, 1, 2, 3]
# Не стоит делать так:
first_list = first_list + second_list
# либо:
first_list += second_list
# потому что такой метод работает МЕДЛЕННЕЕ, чем extend()
Как называются такие конструкции, почему используется точка?
Конструкция типа
list.append()
называется методом. Методы - это функции, которые связаны с определенным объектом и могут быть вызваны через оператор точки.В примере
list.append()
,append()
- это метод объектаlist
, который добавляет новый элемент в конец списка. Оператор точки используется для вызова метода на конкретном объекте. В этом случае, методappend()
вызывается на объектеlist
, чтобы добавить новый элемент в список.Методы являются одним из способов организации и структурирования кода в ООП (объектно-ориентированном программировании). Они позволяют связать функциональность с определенным объектом, что упрощает и улучшает читаемость кода. Кроме того, методы позволяют изолировать и скрыть детали реализации функциональности, что делает код более модульным и поддерживаемым.
Индекс
Это номер позиции элемента в списке.
Индекс номера 5
в списке [1, 5, 45, 0]
будет равен 2, то есть, второй элемент списка.
Взаимодействовать с элементами можно, указав имя списка и в квадратных скобках индекс элемента в списке.
scores = [8, 5, 10, 7, 6]
# удвоить второй элемент можно так:
scores[1] *= 2 # [8, 10, 10, 7, 6]
# последний элемент
print(scores[-1]) # тоже самое можно вывести с помощью len():
# print(scores[len(scores) - 1])
# Таким образом в питоне можно работать со списками СПРАВА НАЛЕВО
# с помощью отрицательных чисел
# можно присваивать переменным значение элементов
tmp = scores[3] # tmp = 7
#МЕТОД .index()👇
# узнать индекс элемента в списке можно так:
scores.index(10) # 2
Здесь тоже, как и везде в программировании, отчет ведется с 0. Поэтому первый элемент списка имеет индекс 0, а не 1.
Строки
# Добавление в список значения переменной ПОСИМВОЛЬНО:
word = python
symbol_list = list(char for char in word)
# ['p', 'y', 't', 'h', 'o', 'n']
#либо попроще (вариант выше мне подсказал ИИ (а ниже - лектор))
symbol_list = list(word)
Срезы (slices)
Срезы позволяют брать нужный кусок списка.
Копии списка
Как раз чтобы избежать ошибок при копировании списков вместо:
some_list = another_list
# потому что в этом случае просто создается новая "ссылка" на список some_list
# копии списка нет
Нужно использовать такой слайс:
some_list = another_list[:]
Куски из списка
То есть, вместо вывода нужного куска из списка циклом:
nums = [x for x in range(1, 101) if (x % 10 == 0)]
# [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
for element_index in range(2, 8):
print(nums[element_index])
# [30, 40, 50, 60, 70]
Можно:
nums = [x for x in range(1, 101) if (x % 10 == 0)]
# [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
print(nums[2:8])
# [30, 40, 50, 60, 70]
# срез по индексам 2,3,4,5,6,7
# как и в range, последняя цифра не учитывается
Также если нужно взять срез до/с какого-то индекса нужно:
nums = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
print(nums[:4]) # срез до 4-го элемента
# [10, 20, 30, 40]
print(nums[6:]) # срез с 6-го элемента до конца списка
# [70, 80, 90, 100]
Шаг в срезе:
nums = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
print(nums[2:8:2]) # срез от 2-го до 8-го элемента с шагом 2
# [30, 50, 70]
print(nums[8:2:-2])
# [90, 70, 50]
Замена с помощью срезов:
nums = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
# Замена первых 3-х элементов на единицы
nums[:3] = [1, 1, 1]
# [1, 1, 1, 40, 50, 60, 70, 80, 90, 100]
# Замена первых 3-х элементов на одну единицу
nums[:3] = [1]
# [1, 40, 50, 60, 70, 80, 90, 100]
Можно взять срез из среза (стэкать их):
nums = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
print(nums[2:8:2])
# [30, 50, 70]
print(nums[2:8:2][::-1])
# [70, 50, 30]
Сдвиг списка
Так, чтобы сдвинуть на 3 шага список numbers
(получить [4, 5, 6, 7, 1, 2, 3] из [1, 2, 3, 4, 5, 6, 7]) - нужно сложить слайсы ([N:] + [:N]) с нужным шагом (3, в нашем случае):
numbers = [1, 2, 3, 4, 5, 6, 7]
numbers[3:] + numbers[:3] # = [4, 5, 6, 7, 1, 2, 3]
# [4, 5, 6, 7] + [1, 2, 3] = [4, 5, 6, 7, 1, 2, 3]
# В итоге:
# [1, 2, 3, 4, 5, 6, 7] -> [4, 5, 6, 7, 1, 2, 3]
Чтобы поменять результат - можно сделать шаг отрицательным и тестить.
Переворачивание списка
Чтобы перевернуть список нужно выбрать весь список (:) и после еще одного двоеточия указать шаг -1 → [::-1]
.
Без второго двоеточия получится “[:-1]
весь срез без последнего элемента”:
# [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
print(nums[::-1]) # REVERSE
# [100, 90, 80, 70, 60, 50, 40, 30, 20, 10]
print(nums[:-1]) # whole list without last element
# [10, 20, 30, 40, 50, 60, 70, 80, 90]
Метод insert
Позволяет добавлять элемент в список по указанному индексом позицию.
Для этого в скобках some_list.insert() указываются индекс и элемент, который нужно вставить:
prog_langs = ['Python', 'Java', 'JS', 'SQL']
prog_langs.insert(1, 'C++')
print(prog_langs)
# ['Python', 'C++', 'Java', 'JS', 'SQL']
Метод remove
Позволяет удалять первый найденный элемент в списке по его имени.
Для этого в скобках some_list.remove() указывается элемент, который нужно удалить:
prog_langs = ['Python', 'Java', 'JS', 'SQL']
prog_langs.remove('Python')
print(prog_langs)
# ['Java', 'JS', 'SQL']
Удаление нескольких элементов
В случаях, когда стоит задача удалить несколько элементов, например, числа 0 из всего списка - метод remove() не подходит потому, что он сам по себе работает не эффективно для такого рода задач. Он после поиска в списке нужного элемента и его удаления сдвигает весь список налево, чтобы заполнить пустоту.
Оператор del
del выполняет обратное присваивание (противоположную присваиванию операцию): открепляет имя от объекта и может освободить место объекта в памяти, если это имя являлось последней ссылкой на него.
>>> nums = [111, 222, 333, 444, 555]
>>> del nums[-1]
>>> nums
[111, 222, 333, 444]
>>> nums = [111, 222, 333, 444, 555]
>>> del nums[1]
>>> nums
[111, 333, 444, 555]
Метод count()
Чтобы узнать, например, кол-во цифры 7 в списке нужно:
numbers = [1, 2, 5, 7, 6, 7, 3]
numbers.count(7) # 2
Функция list()
Добавление строки посимвольно в список:
word = 'Python!'
symbols = list(word)
print(symbols) # ['P', 'y', 't', 'h', 'o', 'n', '!']
Заполнить список с помощью range():
numbers = list(range(5)) # [0, 1, 2, 3, 4]
numbers = list(range(1, 4)) # [1, 2, 3, 4]
Вложенные списки
Пример вложенного списка (и его генерации для наглядности):
numbers = []
num = 1
for _ in range(3):
numbers.append(list(range(num, num + 3)))
num += 3
print(numbers)
# [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Чтобы получить элемент из вложенного списка нужно сначала обратиться к вложенному списку как к элементу (индексом в квадратных скобках) и рядом же поставит квадратные скобки и указать индекс нужного элемента:
word_list = [['a', 0], ['b', 0], ['c', 0]]
# первым идет индекс вложенного списка
# и потом - индекс элемента в этом списке
print(word_list[1][0]) # b
print(word_list[0][1]) # 0
print(word_list[2][0]) # c
List comprehensions
Еще называют генераторами списков, представлением списков, списковыми включениями. Этот механизм юзает итерирование с помощью ключевых слов for/in для генерации списков.
К слову существуют еще dict comprehensions, set comprehensions и т.д.
Синтаксис
[выражение for элемент in итерабельный объект]
[выражение for элемент in итерабельный объект if условие]
Примеры
Простой список из range()
# ВМЕСТО:
numbers = []
for x in range(10):
numbers.append(x)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# НУЖНО:
numbers = [x for x in range(10)]
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Простой список из range() с выражением
# ВМЕСТО:
for x in range(10):
squares.append(x ** 2)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# НУЖНО:
squares = [(x ** 2) for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# скобки для читаемости
Структура записи генерации списков (2-й пример):
x**2
- это expression (выражение)
for x in
- member (переменная цикла)
range(10)
- iterable (итерируемое - функция, строка, список…)
Список из строки
double = [(x * 2) for x in 'abc']
# ['aa', 'bb', 'cc']
Пример функции
some_list = [some_func(some_key, another_key) for x in another_list]
# возвращает значения из функции (работает как x здесь)
Как добавить 3 input-а сразу в список
words = [input("Enter the word: ") for _ in range(3)]
# или
words = [input(f'Enter the word #{word}: ') for word in range(3)]
# ['asd', 'xcv', 'gfhj']
Условия (в представлении списков)
Вместо:
odd_squares = []
for x in range(10):
if x % 2 != 0:
odd_squares.append(x ** 2)
# [1, 9, 25, 49, 81]
Нужно:
odd_squares = [(x ** 2) for x in range(10) if (x % 2 != 0)]
# [1, 9, 25, 49, 81]
Структура фильтрации элементов:
Условие добавили в конце представления, то есть как фильтр.
if (x % 2 != 0)
- условие
Можно даже юзать else:
odd_n_even_squares = [((x ** 2) if (x % 2 != 0) else (x ** 3))
for x in range(10)]
# [0, 1, 8, 9, 64, 25, 216, 49, 512, 81]
# то есть, (x ** 2) если (x % 2 != 0)
# иначе (x ** 3)
# скобки для читаемости
Структура выбора элементов:
<expression> если <условие> иначе <else_expression>
(x + 1) if <условие> else (x - 1)
Потом уже member и iterable.
Можно также записать готовое значение в expression:
some_list = ['some_string' if <условие> else 'another_string'
for _ in range(10)]
Создание списка кортежей
>>> rows = range(1,4)
>>> cols = range(1,3)
>>> cells = [(row, col) for row in rows for col in cols]
>>> for cell in cells:
print(cell)
(1, 1)
(1, 2)
(2, 1)
(2, 2)
(3, 1)
(3, 2)
# Распаковка кортежа:
>>> for row, col in cells:
print(row, col)
1 1
1 2
2 1
2 2
3 1
3 2
.reverse()
Функция .reverse()
изменяет список, но не возвращает его значения:
>>> marxes = ['AAAAA', 'NNNNN', 'ZZZZZ']
>>> marxes.reverse()
>>> marxes
['ZZZZZ', 'NNNNN', 'AAAAA']
Размножение элементов с помощью оператора +
>>> ["blah"] * 3
['blah', 'blah', 'blah']
.sort()
и sorted()
.sort()
сортирует сам список.
sorted()
возвращает отсортированную копию списка.
>>> marxes = ['Groucho', 'Chico', 'Harpo']
>>> sorted_marxes = sorted(marxes)
>>> sorted_marxes
['Chico', 'Groucho', 'Harpo']
>>> marxes.sort()
>>> marxes
['Chico', 'Groucho', 'Harpo']
>>> numbers = [2, 1, 4.0, 3]
>>> numbers.sort()
>>> numbers
[1, 2, 3, 4.0]
Reverse сортировка:
>>> numbers = [2, 1, 4.0, 3]
>>> numbers.sort(reverse=True)
>>> numbers
[4.0, 3, 2, 1]
Изменение одинаковых списков при присваивании с помощью оператора =
>>> a = [1, 2, 3]
>>> b = a
>>> a[0] = 'surprise'
>>> a
['surprise', 2, 3]
>>> b
['surprise', 2, 3]
Список b
тоже изменился, ибо он просто ссылается на тот же список объектов, что и a
.
Решение: Следующая глава
Копирование списков - .copy()
, list()
, слайсы и .deepcopy()
.copy()
, list()
, слайсы
Оригинальный список снова будет присвоен переменной а
. Создам b
с .copy()
, c
- с list()
, а d
- с помощью слайсов:
>>> a = [1, 2, 3]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]
>>> a[0] = 'integer lists are boring'
>>> a
['integer lists are boring', 2, 3]
>>> b
[1, 2, 3]
>>> c
[1, 2, 3]
>>> d
[1, 2, 3]
.deepcopy()
В таких ситуациях:
>>> a = [1, 2, [8, 9]]
>>> b = a.copy()
>>> c = list(a)
>>> d = a[:]
# И при изменении элементов подсписка изменения произведутся и в других списках:
>>> a[2][1] = 10
>>> a
[1, 2, [8, 10]]
>>> b
[1, 2, [8, 10]]
>>> c
[1, 2, [8, 10]]
>>> d
[1, 2, [8, 10]]
нужен .deepcopy()
, чтобы полностью скопировать список, иначе вложенные списки продолжат указывать на те же объекты, а не будут копией.
Короче, нужна полная “глубокая” копия:
>>> import copy
>>> a = [1, 2, [8, 9]]
>>> b = copy.deepcopy(a)
>>> a
[1, 2, [8, 9]]
>>> b
[1, 2, [8, 9]]
>>> a[2][1] = 10
>>> a
[1, 2, [8, 10]]
>>> b
[1, 2, [8, 9]]
Функция .deepcopy() работает с вложенными списками, словарями и т.п.
zip()
Функция
zip()
создает итератор, который объединяет элементы из нескольких источников данных. Работает со списками, кортежами, множествами и словарями для создания списков или кортежей, включающих все эти данные.
>>> list1 = [111, 222, 333, 444]
>>> list2 = [222, 333, 555, 999]
>>> zip(list1, list2)
<zip object at 0x7f48a1be4440>
>>> list(zip(list1, list2))
[(111, 222), (222, 333), (333, 555), (444, 999)]
# Или словарь:
>>> english = 'Monday', 'Tuesday', 'Wednesday'
>>> french = 'Lundi', 'Mardi', 'Mercredi'
>>> dict(zip(english, french))
{'Monday': 'Lundi', 'Tuesday': 'Mardi', 'Wednesday': 'Mercredi'}
Часто используемый сценарий - параллельное итерирование по нескольким последовательностям:
>>> days = ['Monday', 'Tuesday', 'Wednesday']
>>> fruits = ['banana', 'orange', 'peach']
>>> drinks = ['coffee', 'tea', 'beer']
>>> desserts = ['tiramisu', 'ice cream', 'pie', 'pudding']
>>> for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts):
print(day, ": drink", drink, "eat", fruit, "enjoy", dessert)
Monday : drink coffee eat banana enjoy tiramisu
Tuesday : drink tea eat orange enjoy ice cream
Wednesday : drink beer eat peach enjoy pie
Самый длинный список не проитерировался до конца
Из списка desserts не проитерировался последний элемент (
'pudding'
), ибо этот список самый длинный и до конца не проитерируется, пока не увеличишь другие списки до его размера.
LIFO и FIFO
Если вы используете функцию .append()
, чтобы добавить новые элементы в конец списка, и функцию .pop()
, чтобы удалить что-либо из конца того же списка, вы работаете со структурой данных, известной как очередь LIFO (last in, first out - «последним пришел, первым ушел»). Ее чаще называют стеком. Вызов .pop(0)
создает очередь FIFO (first in, first out - «первым пришел, первым ушел»). Это удобный способ собирать данные по мере их поступления и работать либо с самыми старыми (FIFO), либо с самыми новыми (LIFO).
Соус (не весь): Книга “Простой Python” → Глава 7. “Кортежи и списки”