for
¶Создадим список значений возраста респондентов, список age
:
age = [23, 25, 32, 48, 19] # возраст
age
[23, 25, 32, 48, 19]
Элементы списка перечисляются в квадратных скобках через запятую.
Можем создать список имен name
, полностью состоящий из строк:
name = ["Ann", "Nick", "Ben", "George", "James"] # имена
А можем создать смешанный список – список, состоящий из элементов разных типов. Представим, что не очень сознательный исследователь закодировал пропущенные значения в списке текстом, написал «нет ответа»:
mix = [23, 25, "no answer", 32] # все вместе
Элементы разных типов спокойно уживаются в списке: Python не меняет тип элементов. Все элементы, которые являются строками, останутся строками, числа – числами, а сам список будет обычным списком:
type(mix)
list
У списка всегда есть длина – количество элементов в нём. Длина определяется с помощью функции len()
.
len(age) # пять элементов
5
Если список пустой, то, как несложно догадаться, его длина равна нулю:
empty = []
len(empty)
0
Раз список состоит из элементов, к ним можно обратиться по отдельности. Главное, нужно помнить, что нумерация в Python начинается с нуля, а не с единицы. Существует несколько обоснований, почему это так, с одним из них мы познакомимся чуть позже, когда будем обсуждать срезы (slices).
age[0] # первый элемент age
23
Порядковый номер элемента в списке называется индексом. Далее, чтобы не путаться, будем разделять термины: порядковые числительные останутся для обозначения номера элемента в нашем обычном понимании, а индексы – для обозначения номера элемента в Python. Например, если нас будет интересовать элемент 25 из списка age
, мы можем сказать, что нас интересует второй элемент или элемент с индексом 1:
print(age)
print(age[1])
[23, 25, 32, 48, 19] 25
Если элемента с интересующим нас индексом в списке нет, Python выдаст ошибку, а точнее, исключение, IndexError
.
age[5]
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-9-6dda8ec0be36> in <module> ----> 1 age[5] IndexError: list index out of range
А как обратиться к последнему элементу списка, да так, чтобы код работал и в случае, когда мы изменим длину списка? Давайте подумаем. Длина списка age
, как мы уже убедились, равна 5, но нумерация самих элементов начинается с нуля. Поэтому:
age[len(age)-1] # последний элемент - 19
19
Конечно, в том, что нумерация элементов в списке начинается с нуля, есть некоторое неудобство – индекс последнего элемента не совпадает с длиной списка. Но, на самом деле, обращаться к последнему элементу списка можно и по-другому: считать элементы с конца!
age[-1] # последний элемент - он же первый с конца
19
Отрицательные индексы элементов в Python – абсолютно нормальная вещь. Можем так же получить второй элемент с конца:
age[-2]
48
Список – изменяемый объект в Python (mutable type). Элементы списка можно изменять, внося изменения прямо в нужный список, то есть не создавая при этом новую переменную.
Для сравнения: объекты, с которыми мы работали ранее, были неизменяемыми. Для того, чтобы перезаписать значение числовой переменной a
, нужно было явно задействовать оператор присваивания =
:
# было 2
a = 2
# стало 3
a = a + 1
print(a)
3
Для того, чтобы перезаписать значение строки тоже без присваивания не обойтись:
s = "Welcome"
s.lower()
print(s) # пока все по-старому
Welcome
s = s.lower()
print(s) # после приравнивания изменилось
welcome
Теперь создадим список nums
и изменим его элемент:
nums = [1, 5, 8, 9]
nums[1] = 15
nums # заменили второй элемент на 15
[1, 15, 8, 9]
А еще можно дописывать элементы в конец списка. Для этого существует два метода: .append()
и .extend()
. Метод .append()
используется для присоединения одного элемента, .extend()
– для добавления целого списка.
nums.append(10) # добавили 10
nums
[1, 15, 8, 9, 10]
nums.extend([20, 30]) # добавили 20 и 30
nums
[1, 15, 8, 9, 10, 20, 30]
Методы .append()
и .extend()
приписывают значения только в конец списка. Для добавления элементов в любое другое место существует метод .insert()
, и мы поговорим о нем чуть позже.
Другой способ добавлять элементы в список – склеивать их, то есть использовать операцию, которая называется конкатенацией. В этом смысле списки очень похожи на строки, и для их конкатенации тоже используется знак +
:
[1, 2, 3] + [9, 10]
[1, 2, 3, 9, 10]
Запись через +
кажется очень интуитивной и заманчивой, но не стоит ей часто пользоваться, особенно, когда списки большие и когда списков много. При такой конкатенации списков происходит создание нового списка, который «склеивается» из отдельных частей, чего не происходит при использовании extend
: там элементы просто дописываются в уже существующий список. Поэтому приписывание одного списка в конец другого быстрее и эффективнее делать именно через extend
.
Мы уже познакомились с тем, как выбирать отдельные элементы из списка, однако мы еще не обсудили, как выбирать несколько элементов подряд. Такие части списков называются срезами (slices). Индексы элементов, которые должны войти в срез, указываются в квадратных скобках, через двоеточие (начало
: конец
).
print(nums)
[1, 15, 8, 9, 10, 20, 30]
nums[1:3] # левый конец включается, а правый нет
[15, 8]
Важно: правый конец не включается в срез! В срез выше вошли элементы с индексами 1 и 2, элемент с индексом 3 включен не был.
Если мы хотим задать только начало или конец среза, один из индексов легко можно опустить:
print(nums[1:])
print(nums[3:])
print(nums[:2])
[15, 8, 9, 10, 20, 30] [9, 10, 20, 30] [1, 15]
Тут мы подходим к тому, почему нумерация элементов в Python начинается с нуля. В частности, для удобных срезов. Если нам нужны первые два элемента списка, нам не нужно долго думать и сдвигать номера элементов на единицу, достаточно просто написать, например, age[:2]
.
Можно ли сделать срез, который будет включать в себя весь список? Легко!
nums[:] # опускаем все индексы
[1, 15, 8, 9, 10, 20, 30]
Изменять элементы списка необязательно по одному, можно задействовать срезы.
print(nums)
nums[1:3] = [25, 26] # заменим 1 и 2 элементы
print(nums)
[1, 15, 8, 9, 10, 20, 30] [1, 25, 26, 9, 10, 20, 30]
Длина списка, на который мы заменяем срез, не обязательно должна совпадать с длиной среза. Можно взять список с большим числом элементов, тогда исходный список расширится, а можно с меньшим – список сузится. Замены остальных элементов при этом не произойдет, новый срез просто «вклинится» в середину списка.
nums[1:3] = [18, 32, 45]
print(nums)
[1, 18, 32, 45, 9, 10, 20, 30]
На данный момент мы достаточно хорошо познакомились со списками. Но списки не так просты, как кажется. Давайте попробуем сделать следующее: скопировать один список в другой путем присваивания.
L1 = [1, 8, 9, 4]
L2 = L1 # сохранили список L1 в L2
print(L1)
print(L2)
[1, 8, 9, 4] [1, 8, 9, 4]
Пока все ожидаемо. Теперь изменим элемент списка L2
с индексом 3:
L2[3] = 5
print(L2)
[1, 8, 9, 5]
А теперь посмотрим на список L1
.
print(L1)
[1, 8, 9, 5]
Несмотря на то, что список L1
мы не трогали, он изменился точно так же, как и список L2
! Что произошло? На самом деле, когда мы записали L2 = L1
, мы скопировали не сам список, а ссылку на него. Другими словами, проводя аналогию с папкой и ярлыком, вместо того, чтобы создать новую папку L2
с элементами, такими же, как в L1
, мы создали ярлык L2
, который сам по себе ничего не представляет, а просто ссылается на папку L1
.
Так как же тогда копировать списки? Во-первых, у списков есть метод .copy()
.
# дубль два
L1 = [1, 8, 9, 4]
L2 = L1.copy()
# теперь делаем что угодно
L2[3] = 100
print(L1)
print(L2) # все нормально
[1, 8, 9, 4] [1, 8, 9, 100]
Во-вторых, можно сделать срез и «срезать» весь список:
# дубль три
L1 = [1, 8, 9, 4]
L2 = L1[:] # полный срез
# теперь делаем что угодно
L2[3] = 100
print(L1)
print(L2) # все нормально
[1, 8, 9, 4] [1, 8, 9, 100]
for
¶Раз есть списки, хочется научиться пробегаться по их элементам. Например, выводить на экран не весь список age
сразу, а постепенно, каждый элемент с новой строчки. Для этого есть циклы. Рассмотрим цикл for
.
for i in nums:
print(i)
1 18 32 45 9 10 20 30
Как устроен цикл выше? Кодом выше мы доносим до Python мысль: пробегайся по всем элементам списка age
(for i in age
) и выводи каждый элемент на экран (print(i)
). Вообще любой цикл for
имеет такую структуру: сначала указывается, по каким значениям нужно пробегаться, а потом, что нужно делать. Действия, которые нужно выполнить в цикле, указываются после двоеточия в for
– эта часть назвается телом цикла.
Буквы в конструкции for
могут быть любые, совсем необязательно брать букву i
. Python сам поймет, что мы имеем в виду, запуская цикл.
# element вместо i
for element in nums:
print(element)
1 18 32 45 9 10 20 30
Давайте, используя цикл, создадим новый список.
nums2 = [] # новый список
for i in nums:
nums2.append(i * 2)# добавляем в него значения из nums, умноженные на 2
nums2
[2, 36, 64, 90, 18, 20, 40, 60]
Конечно, циклы нужны не только для того, чтобы работать со списками. С помощью циклом можно решить любую задачу, которая требует повторения одинаковых действий. Вспомним задачу с семинара про питона, который греется на солнышке и каждый день увеличивает время пребывания на солнце. Тогда мы решали эту задачу, перезапуская ячейку с кодом несколько раз. Теперь воспользуемся циклом.
# создадим список с номерами дней
days = [2, 3, 4, 5, 6, 7, 8, 9 , 10]
# начальное значение времени, которое питон проводит на солнце
time = 1
print(1, time)
# теперь будем изменять значение time в цикле
# и выводить на экран номер дня и время
for d in days:
time = time + 3
print(d, time)
1 1 2 4 3 7 4 10 5 13 6 16 7 19 8 22 9 25 10 28
range()
¶На самом деле, можно было поступить еще проще. В Python есть функция range()
, которая позволяет перебирать целые числа на заданном промежутке, не создавая при этом сам список чисел.
# пример
for j in range(0, 6):
print(j)
0 1 2 3 4 5
Правый конец заданного в range()
промежутка не включается, будьте бдительны. В примере выше на экран были выведены числа от 0 до 5, число 6 включено не было. Применим range()
к нашей задаче про питона:
time = 1
print(1, time)
for d in range(2, 11):
time = time + 3
print(d, time)
1 1 2 4 3 7 4 10 5 13 6 16 7 19 8 22 9 25 10 28
Если мы хотим посмотреть на то, какие значения будут в range()
, придется превратить его в список:
range(0, 3) # не поспоришь, но бесполезно
range(0, 3)
list(range(0, 3)) # значения внутри range
[0, 1, 2]
Полезный факт: если нас интересуют числа на промежутке, начиная с нуля, в range()
левый конец можно не указывать, 0 будет выбран по умолчанию.
list(range(5))
[0, 1, 2, 3, 4]