Математический практикум по Питону.
Вводится базовый объект Питона (Python) список (list). Показано как его создавать, проверять принадлежность элемента, обращаться к отдельным элементам (сложные индексы), и искать индекс элементов. Рассказано про цикл while и цикл for применительно к спискам и о важной их составляющей else.
Ключевые слова: list, in, len, type, index while, for и else.
Это предварительная версия! Любые замечания приветствуются.
Вычисления над отдельными числами это хорошо, но нам понадобиться список, например, чисел. Списки нужны хотябы для того, чтобы хранить наши данные (котировки акций, список товаров и тому подобное). Список позволяет хранить несколько значений, в общем случае, разных типов как если бы они были разными перемеными. Главное, что список подддерживает добавление новых элементов (в его конец), а также извлечение и удаление подсписков.
[] # Пустой список.
[]
[77] # Список из одного элемента.
[77]
Создаем список из 6 элементов.
[4, 8, 15, 16, 23, 42]
[4, 8, 15, 16, 23, 42]
В момент создания список конечно же можно проинициализировать выражениями.
[ 2 + 5, 7 - 9] # Инициализировать, т.е. задать.
[7, -2]
Чтобы были понятны принципы работы с ним, нужно его все-таки присвоить переменной.
# Присвоили переменной l список чисел.
l = [4, 8., 15, 16, 23, 42] # Внимание на второе число!
type( l ) # Выведем его тип.
# Видно, что он отличается от ранее используемых числовых типов.
list
Выведем элементы списка.
l
[4, 8.0, 15, 16, 23, 42]
Определим количество элементов в списке
# Оператор len позволяет узнать длину списка, т.е. количество элементов в нем.
len( l ) # Ранее встречался с str, строчкой.
6
Через явное преобразование.
# По аналогии с int, float, ...
list([11, 55, 10 - 13, -7, 8]) # list это объект.
[11, 55, -3, -7, 8]
По аналогии для строчки.
list('string')
# т.е. символы превращаются в элементы списка.
['s', 't', 'r', 'i', 'n', 'g']
Очевидно, важно иметь возможность проверки принадлежности элемента списку. Делается это так.
4 in l # Проверяем 4 принадлежит списку l.
True
type( 4 in l ) # Возращается нам знакомый тип bool.
bool
5 in l
False
При этом выполняются нужные преобразования типа.
l
[4, 8.0, 15, 16, 23, 42]
# Речь о том, что в списк 8., т.е. float.
4.0 in l, 8 in l, 8.0 in l # А здесь 8, т.е. тип int.
(True, True, True)
# Как легко видеть 9 не принадлежит списку:
9 in l
False
# Можно проверить и отрицание:
9 not in l
True
# Можно и так:
not 9 in l
# Но правильнее not in.
True
9 not in l
True
# для полноты картины:
not 4 in l
False
Соответственно применительно в ветвлению.
if 9 not in l:
print('Элемента нет в списке')
Элемента нет в списке
el = 4
#el = 9
if el not in l:
print('Элемента', el, 'нет в списке')
else:
print('Элемент', el, 'в списке')
Элемент 4 в списке
упр. Сформировать список корней квадратного уравнения.
К списку можно относится как проиндексированныйм (пронумерованным) переменным.
# Напомним содержимое списка:
l
[4, 8.0, 15, 16, 23, 42]
Число в квадратных скобках указывает на индекс в списке.
# Внимание, элементы списка нумеруются с 0.
l[3]
16
Подумаем над данной записью:
[4, 8.0, 15, 16, 23, 42][3]
# <список> <индекс извлекаемого элемента>
16
Индекс должен соответствовать существующему элементу списка.
# Иначе будет ошибка.
l[7] # В списке нет такого индекса.
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-27-8dafd2373c05> in <module> 1 # Иначе будет ошибка. ----> 2 l[7] # В списке нет такого индекса. IndexError: list index out of range
len(l)
6
Раз нумерация индекса начинается с нуля, а количество элементов 6, то последний валидный индекс равен 5.
l[6] # Это граничный элемент.
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-29-866e352db5be> in <module> ----> 1 l[6] # Это граничный элемент. IndexError: list index out of range
# Этот индекс уже валидный.
l[5]
42
В общем случае через оператор len
l[len(l)]
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-31-95e9c62a3baf> in <module> ----> 1 l[len(l)] IndexError: list index out of range
l[len(l)-1]
42
Список и тип элементов
l
[4, 8.0, 15, 16, 23, 42]
l[3]
16
type( l[3] ) # Но, тип самого элемента списка ествественно свой, числовой.
int
Тип у разных элементов списка может быть разным.
type( l[1] ), l[1] # float
(float, 8.0)
type( l[3] ), l[3] # int
(int, 16)
Разнородные элементы
В заключении обсуждения списков отмечу, что их элементы не обязаны иметь один и тот же тип
ll = [2, "строка текста", 6]
ll
[2, 'строка текста', 6]
# Элемент с индексом 0 и 2 содержит
ll[0], ll[2]
(2, 6)
type(ll[0]), type(ll[2]) # В тоже время.
(int, int)
ll[1], type( ll[1] ) # А элемент с индексом 1 являтся строчкой.
('строка текста', str)
Последнее показывает, что списки могут соедржать совершенно разнородные объекты. В этом смысле они полноценные.
Поэтому, естественно, что списки могут содержать списки.
a = [ 2, 3, [4, 5], 8] # Список из трех элементов.
a
[2, 3, [4, 5], 8]
# Нулевой, первый и третьий содержат числа:
a[0], a[1], a[3]
(2, 3, 8)
# А вот второй элемент сам является списком:
a[2], type(a[2])
([4, 5], list)
# Обращаемся ко второму элементу вложенного списка:
a[2][1]
5
Для других элементов это не прокатит
a[1][2]
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-46-1cc78e3cc843> in <module> ----> 1 a[1][2] TypeError: 'int' object is not subscriptable
Можно конечно проверить принадлежность списку более сложных объектов.
[4, 5] in a
True
a[2]
[4, 5]
[-5,5] in a
False
Значение
Можно присловить другое значение элементу списка.
l
[4, 8.0, 15, 16, 23, 42]
l[2]
15
Отсылка к проиндексированным переменным.
l[2] = 1 # Присвоение элементу с индексом 2.
l # Выводим значение переменной l, т.е. списк чисел
[4, 8.0, 1, 16, 23, 42]
# Присвоение последнему элементу списка:
l[len(l)-1] = 11
l
[4, 8.0, 1, 16, 23, 11]
Упражнение: Заполнить список первыми n простыми числами. n Вводится.
Для вложенных списков
a
[2, 3, [4, 5], 8]
a[2][0] = -5 # Можно и что-то присвоить.
a
[2, 3, [-5, 5], 8]
Рекурсия из списков
ll=[1,2,3] # Создаем список.
ll
[1, 2, 3]
ll[1] = ll # Присваиваем его второму элементу.
# С интересом выводим.
ll # Ну хорошо, что система не повисла.
[1, [...], 3]
# От таких фокусов подальше...
ll[1] = "kkk" # присваиваем строчку.
ll
[1, 'kkk', 3]
l
[4, 8.0, 1, 16, 23, 11]
Можно указывать диапазон индексов: от и до.
# от:до
l[3:5] # Причем, "до" невключительно, т.е. в данном случае индексы: 3 и 4.
[16, 23]
l[3:4]
[16]
При задании интервала индексов диапазон может выходить за пределы списка.
l[3:100] # В таком случае диапазон будет укорочен.
# Ошибкой это считаться не будет.
[16, 23, 11]
# Если после двоеточия числа нет,
l[2:] # то значит нужно идти до конца списка.
[1, 16, 23, 11]
# Если числа нет до двоеточия,
l[:4] # то значит новый список начинется с начала данного.
[4, 8.0, 1, 16]
Можно оба числа опустить:
l[:]
[4, 8.0, 1, 16, 23, 11]
Явный вид указания последнего индекса, который не включительный.
l[2:len(l)] # Как надо.
[1, 16, 23, 11]
# Если чуть меньше,
l[2:len(l) - 1] # то не все элементы (пропущен последний).
[1, 16, 23]
# Если начало начинается после конца,
l[5:1] # то конечно получим "пустое множество", т.е. список.
[]
# Даже если границы совпадают,
l[4:4] # всеравно пусто.
[]
Итого, если не указывать явно одну из границ диапазона индексов, то индексы берутся по максимуму.
# в даном случае, соответственно,
l[:5], l[3:] # с начала и до конца.
([4, 8.0, 1, 16, 23], [16, 23, 11])
Вложенность индексаций
Для упрощения понимания воспользуемся переменной
w = l[:5]
Как мы видим, срез списка по сложному индексу является списком.
type(w)
list
Вот его элементы
w
[4, 8.0, 1, 16, 23]
Можем повторить срез для этого нового списка.
w[:2]
[4, 8.0]
l[:5][:2]
[4, 8.0]
l
[4, 8.0, 1, 16, 23, 11]
l[:5][2:]
[1, 16, 23]
Не путать с вложенностью списков!
a
[2, 3, [-5, 5], 8]
a[2][:1]
[-5]
Присвоение сразу нескольким элементам списка
l
[4, 8.0, 1, 16, 23, 11]
l[1:3] # Смотрим какие значения были до.
[8.0, 1]
# Присвоить можно и диапазону значений сразу:
l[1:3] = 23, 7
l
[4, 23, 7, 16, 23, 11]
l[2:4] = [4, -7] # А можно и так.
l
[4, 23, 4, -7, 23, 11]
На самом деле происходит замена выбранной части списка на элементы другого списка. Поэтому если взять элементов больше отмеченном диапазоне, то список расширится.
l[1:3] = 23, 7, -6 # Взяв больше эелементов
l # список расширился.
[4, 23, 7, -6, -7, 23, 11]
l[2:3] = [4, -7, 11, 23, 25]
l # Опять список расшиширтся.
[4, 23, 4, -7, 11, 23, 25, -6, -7, 23, 11]
l[2:8] = [4, -7]
l # А так сократится.
[4, 23, 4, -7, -7, 23, 11]
l=[4, 8.0, 1, 16, 23, 11]
l[1:100]
[8.0, 1, 16, 23, 11]
В расширенном варианте можно указать шаг увеличения индекса.
# Шаг указывается после второго двоеточия.
l[1:100:2] # В данном случае он равен 2,
# т.е. каждый второй.
[8.0, 16, 11]
# Ествественно в расширенном варинте тоже можно опускать числа.
l[1::2] # Смысл будет прежним.
[8.0, 16, 11]
# По умолчанию шаг равен 1,
l[1::1] # но можно явно это указать.
[8.0, 1, 16, 23, 11]
l[1::]
[8.0, 1, 16, 23, 11]
l[1:]
[8.0, 1, 16, 23, 11]
Отрицательный индекс
Казалось бы что будет если взять отрицательный индекс? Мы же выйдем за границу...
l
[4, 8.0, 1, 16, 23, 11]
Отмечу, что элементы списка можно нумеровать и с конца. Для этого используются отрицательные индексы.
l[-1] # -1 соответствует первому элементу с конца, т.е. последнему.
11
Чтобы пронумеровать с конца сложим отрицательный индекс с длиной списка.
(-1)+len(l)
5
l[5]
11
l[(-1)+len(l)]
11
l[-2] # Предпоследний элемент или иначе второй элемент с конца.
23
Но все-таки всему есть предел...и отрицательным индексам тоже.
l[-7] # Вышли на диапазон
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-103-64c36fa63084> in <module> ----> 1 l[-7] # Вышли на диапазон IndexError: list index out of range
Выходим за диапазон, если после прибавления длины списка получившиеся число по-прежнему отрицательное.
(-7)+len(l)
-1
А это допустимо.
l[-6]
4
Такие элементы также можно использовать при задании диапазона индексов.
l[:-2] # В данном случае, извлекутся все элементу кроме двух посдних.
[4, 8.0, 1, 16]
Отрицательный шаг
l
[4, 8.0, 1, 16, 23, 11]
Шаг тоже может быть отрицательный.
l[::-1] # В даном случае получится список в обратном порядке.
[11, 23, 16, 1, 8.0, 4]
# Аналог начало идет после конца.
l[-1:2] # Поэтому пустой список.
[]
Но можно же шаг взять отрицательным.
# Тогда список будет обойден в обратную сторону:
l[-1:2:-1] # -1(5), -2(4), -3(3), -4 (2).
# В скобках реальный индекс.
[11, 23, 16]
l[-1:-4:-1] # Но так будет понятнее.
[11, 23, 16]
l[-2:3:-1] # Очередной пример для закрепления материала.
[23]
l
[4, 8.0, 1, 16, 23, 11]
Упр. Как переставить в обратном порядке элементы списка с четным индексом? На нечетных останутся неизменными. Пример: [23, 23, 4, -7, 4, 11]
Напомним элементы списка.
l
[4, 8.0, 1, 16, 23, 11]
l.index(23) # Ищет индекс первого вхождения числа 23.
4
l[1] # Проверим.
8.0
l.index(23, 2) # Ищет первое вхождение числа 23 начиная со второго индекса.
4
l[4] # Еще раз проверим.
23
Возьмем отсутствующий в списке элемент
55 in l
False
Вызовет ошибку поиска.
l.index(55)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-120-9b758868fea8> in <module> ----> 1 l.index(55) ValueError: 55 is not in list
l.index(23, 5)
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-121-c275174ac101> in <module> ----> 1 l.index(23, 5) ValueError: 23 is not in list
т.е. не надо искать в списке индекс того чего там нет.
Можно указать и диапазон для поиска элемента
l.index(23, 2, 5) # Ищет первое вхождение начиная со второго индекса по пятый невключительно.
4
l
[4, 8.0, 1, 16, 23, 11]
i = 0
while l[i]!=23: # Условие цикла. Тело выполняется пока условие истинно.
i=i+1 # Тело цикла.
print(i)
4
Тот же цикл для отсутствующего элемента
i = 0
while l[i]!=55: # Условие цикла. Тело выполняется пока условие истинно.
i=i+1 # Тело цикла.
print(i)
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) <ipython-input-125-7f7d23a03235> in <module> 1 i = 0 ----> 2 while l[i]!=55: # Условие цикла. Тело выполняется пока условие истинно. 3 i=i+1 # Тело цикла. 4 5 print(i) IndexError: list index out of range
Чтобы не было ошибки:
i = 0
while i < len(l) and l[i]!=55: # Условие цикла. Тело выполняется пока условие истинно.
i=i+1 # Тело цикла.
print(i)
6
Добавив доп условие цикл усложнился. И это еще else к циклу не добавлен... Лучше такую конструкцию со списками не использовать.
Со списками лучше использовать цикл for, а не while. Для них есть специальный вариант под обход списка.
summ = 0
i = -1 # Почему?
for el in l:
print("Элемент списка", el)
summ += el
i += 1
if el == -7:
break
Элемент списка 4 Элемент списка 8.0 Элемент списка 1 Элемент списка 16 Элемент списка 23 Элемент списка 11
i
5
need = -7
for el in l:
print("Элемент списка", el)
if el == need:
# Делаем что то важное....
print("found!")
break
else:# Ао аналогии с while
print("Элемент не найден")
Элемент списка 4 Элемент списка 8.0 Элемент списка 1 Элемент списка 16 Элемент списка 23 Элемент списка 11 Элемент не найден
need = 15
Найдем 2 элемент равный need
l[4]=8
l
[4, 8.0, 1, 16, 8, 11]
need = 8
cnt = 2
for el in l:
print("Элемент списка", el)
if el == need:
if cnt > 1:
cnt-=1
continue# Переходим на начало цикла к следующему элементу.
print("found!")
break
else:# Ао аналогии с while
print("Элемент не найден")
Элемент списка 4 Элемент списка 8.0 Элемент списка 1 Элемент списка 16 Элемент списка 8 found!
С объектом строка мы уже ранее встречались. В данной заметки будут показаны дополнительные с ней взаимодействия.
s = 'некая строка'
list('строка 44') # Отмечу, что пробел тоже является символом.
['с', 'т', 'р', 'о', 'к', 'а', ' ', '4', '4']
Индекс работает также как и для списка, list. Не будем подробно останавливаться. Строка текста тоже на самом деле может быть проиндексирована, т.е. можно считать букву по индексу.
"индекс"[1] # Считываем второй символ строки.
'н'
s, type(s) # Напомню содержимое.
('некая строка', str)
s[4] # Тоже для переменной типа str.
'я'
s[-1] # Последний символ.
'а'
# Даже такое.
s[1::2] # Результат тоже строка.
'еа тоа'
type(s[1::2])
str
Поэтому, как и для списков можно опять взять сложный индекс.
s[1::2][1] # Индекс от индекса.
'а'
Но есть и существенные отличия. Например, строка неизменяемый объект, константный.
s[4] # Посмотрим значение (символ).
'я'
Но присвоить новое значение нельзя.
s[4] = 'a' # Присвоить новое значение нельзя.
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-145-98d801555fe2> in <module> ----> 1 s[4] = 'a' # Присвоить новое значение нельзя. TypeError: 'str' object does not support item assignment
dat = '... Город: Москва'
# Поиск подстроки.
dat.find('Город: ',0,-1) #
4
dat[4:]
'Город: Москва'
dat.rfind('Город: ')
4
'17:23:22'.find(':'), '17:23:22'.rfind(':')
(2, 5)
'17:23:22'.index(':'), '17:23:22'.rindex(':')
(2, 5)
'17:23:22'[5:]
':22'
Разница между find и index.
# В случае, если строчка не найдена,
dat.find('Страна') # то возвращается -1.
-1
dat.index('Страна') # Бросает исключение.
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-154-48644b652a12> in <module> ----> 1 dat.index('Страна') # Бросает исключение. ValueError: substring not found
tt = 'с 12 час 23 мин по 14 час 31 мин'
Извлекаем время. Пользуемся тем, что сначала идет время начальное, а потом конечное.
fr = tt.find('час') # from с
tl = tt.rfind('час') # till до
tt[:fr], tt[:tl]
('с 12 ', 'с 12 час 23 мин по 14 ')
ch = tt[:fr]
ch
'с 12 '
ch[-3:-1]
'12'
# Печатаем найденное время.
tt[fr-3:fr-1], tt[tl-3:tl-1]
('12', '14')
Покажем практический пример применения при скачивании файлов.
ss = 'https://math.msu.ru/sites/all/themes/maththeme/img/logo-mm.png'
ss[ss.rfind('/')+1:]
'logo-mm.png'
from urllib import request
request.urlretrieve(ss, ss[ss.rfind('/')+1:])
('logo-mm.png', <http.client.HTTPMessage at 0x7f3e845bc860>)
Чистка пробелов
s = ' q q \t \n '
# Убираем лишние 'пробелы' в начале и конце строки.
s.strip()
'q q'
# Убераем пробелы только с одного из концов строки.
s.lstrip(), s.rstrip()
('q q \t \n ', ' q q')
Сама строка не меняется.
s
' q q \t \n '
Упражнение. Примени чистку пробелов для чистки введенного имени пользователя. Выведите что-то о нем чтобы было видно что пробелу удалены.
Разбиение на подстроки и объединение
Иногда бывает нужным разбить строчку по пробелам.
# Разбиение по пробелу.
имена = 'Маша Катя Лена'.split(' ') # rsplit splitlines
имена
['Маша', 'Катя', 'Лена']
Возвращается как раз список.
type(имена), имена[1]
(list, 'Катя')
На самом деле можно и по другому символу разбить строку.
'Маша; Катя; Лена'.split('; ') # '; ' разделитель из двух символов.
['Маша', 'Катя', 'Лена']
# По одному пробелу.
'Маша Катя Лена '.split(' ') # В списке появилась пустая строка.
['Маша', 'Катя', '', 'Лена', '']
А бывает, что нужно наоборот объеденить список строк. Между объединяемыми строками будем вставлена нужная строка.
# Возьмем список имен.
names = ['Максим', 'Дима', 'Алексей', 'Олег']
Объедим список строчек посредством строки.
', '.join(names) # В данном случае ', '
'Максим, Дима, Алексей, Олег'
', '.join(names) + '.' # Добавим и концевую точку.
'Максим, Дима, Алексей, Олег.'
# Совсем педантично.
print(', '.join(names[:-1]) + ' и ' + names[-1] + '.')
Максим, Дима, Алексей и Олег.
Кодировки
q = 'aa'.encode('utf16') # На выходе тип -- строка байтов.
q, type(q), len(q)
(b'\xff\xfea\x00a\x00', bytes, 6)
q[4] # Выведем четвертый элемент байтовой строки
97
b'abc'
b'abc'
win1251 = 'винда'.encode('windows-1251')
win1251
b'\xe2\xe8\xed\xe4\xe0'
win1251.decode('windows-1251')
'винда'
win1251.decode('koi8-r') # Старые добрые времена...
'БХМДЮ'