
3. Списки и цикл for - Jupyter Notebook
.pdf
19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
Списки и цикл for
Знакомство со списками
Создадим список значений возраста респондентов, список age :
In [1]:
1 age = [23, 25, 32, 48, 19] # возраст 2 age
Out[1]:
[23, 25, 32, 48, 19]
Элементы списка перечисляются в квадратных скобках через запятую.
Можем создать список имен name , полностью состоящий из строк:
In [2]:
1 name = ['Анна', "Виктор", "Дмитрий", "Алёна", "Павел"] # имена
2 name
Out[2]:
['Анна', 'Виктор', 'Дмитрий', 'Алёна', 'Павел']
А можем создать смешанный список ‒ список, состоящий из элементов разных типов. Представим, что не очень сознательный исследователь закодировал пропущенные значения в списке текстом, написал "нет ответа":
In [3]:
1 mix = [23, 25, "нет ответа", 32, "нет ответа"] # все вместе
Элементы разных типов спокойно уживаются в списке: Python не меняет тип элементов. Все элементы, которые являются строками, останутся строками, числа ‒ числами, а сам список будет обычным списком:
In [5]:
1 type(mix)
Out[5]:
list
Для тех, кто работал в R: векторы в R очень похожи на списки в Python. Но есть важное отличие. В R, вектор, содержащий как числа, так и строки, превратился бы в текстовый вектор ‒ вектор типа character . Например, mix в R выглядел бы так:
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
1/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
"23", "25", "нет ответа", "32", "нет ответа"
У списка всегда есть длина ‒ количество элементов в нем. Длина определяется с помощью функции len() .
In [4]:
1 len(age) # пять элементов
Out[4]:
5
Для тех, кто привык к R: просто len , не length !
Если список пустой, то, как несложно догадаться, его длина равна нулю:
In [2]:
1 empty = []
2 len(empty)
Out[2]:
0
Раз список состоит из элементов, к ним можно обратиться по отдельности. Главное, нужно помнить, что нумерация в Python начинается с нуля, а не с единицы. Существует несколько обоснований, почему это так, с одним из них мы познакомимся чуть позже, когда будем обсуждать срезы (slices).
In [3]:
1 age[0] # первый элемент age
Out[3]:
23
Порядковый номер элемента в списке называется индексом. Далее, чтобы не путаться, будем разделять термины: порядковые числительные останутся для обозначения номера элемента в нашем обычном понимании, а индексы ‒ для обозначения номера элемента в Python. Например, если нас будет интересовать элемент 25 из списка age , мы можем сказать, что нас интересует второй элемент или элемент с индексом 1:
In [11]:
1 |
print(age) |
2 |
print(age[1]) |
[23, 25, 32, 48, 19] 25
Если элемента с интересующим нас индексом в списке нет, Python выдаст ошибку, а точнее,
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
2/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
исключение, |
IndexError . |
In [9]:
1 age[7]
---------------------------------------------------------------------------
IndexError Traceback (most recent call last) <ipython-input-9-979e3832d406> in <module>()
----> 1 age[7]
IndexError: list index out of range
А как обратиться к последнему элементу списка, да так, чтобы код работал и в случае, когда мы изменим длину списка? Давайте подумаем. Длина списка age , как мы уже убедились, равна 5, но нумерация самих элементов начинается с нуля. Поэтому:
In [12]:
1 age[len(age)-1] # последний элемент - 19
2
Out[12]:
19
Конечно, в том, что нумерация элементов в списке начинается с нуля, есть некоторое неудобство ‒ индекс последнего элемента не совпадает с длиной списка. Но, на самом деле, обращаться к последнему элементу списка можно и по-другому: считать элементы с конца!
In [13]:
1 age[-1] # последний элемент - он же первый с конца
Out[13]:
19
Отрицательные индексы элементов в Python ‒ абсолютно нормальная вещь. Можем так же получить второй элемент с конца:
In [14]:
1 age[-2]
Out[14]:
48
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
3/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
In [15]: 1 age
Out[15]:
[23, 25, 32, 48, 19]
Изменение и добавление элементов
Список ‒ изменяемый объект в Python. Элементы списка можно заменять, внося изменения прямо в нужный список (не создавая при этом новый):
In [29]:
1 age[0] = 99 # заменили первый элемент
2 age
Out[29]:
[99, 25, 32, 48, 19]
А еще можно дописывать элементы в конец списка. Для этого существует два метода: .append() и
.extend() . Метод .append() используется для присоединения одного элемента, .extend() ‒ для добавления целого списка. Для примера создадим список nums :
In [50]:
1 nums = [1, 5, 8, 9]
In [51]:
1 nums.append(10) # добавили 10
2 nums
Out[51]:
[1, 5, 8, 9, 10]
In [52]:
1 nums.extend([12, 13]) # добавили 12 и 13 2 nums
Out[52]:
[1, 5, 8, 9, 10, 12, 13]
Приписывать значения можно и к пустому списку. Это нам пригодится, когда мы будем создавать новые списки на основе старых, используя циклы и списковые включения.
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
4/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
||
In [46]: |
|
||
1 |
|
L = [] |
|
|
|
||
2 |
|
L.append(6) |
|
3 |
|
L.append(8) |
|
4 |
|
L |
|
|
|
|
|
Out[46]:
[6, 8]
Методы .append() и .extend() приписывают значения только в конец списка. Для добавления элементов в любое другое место существует метод .insert() , и мы поговорим о нем чуть позже.
Важно: если поменять местами .append() и .extend() , код либо не будет работать (случай 1), либо будет работать не так, как хочется (случай 2).
In [53]:
1 nums
Out[53]:
[1, 5, 8, 9, 10, 12, 13]
In [55]:
1 nums.extend(6) # случай 1: один элемент не добавится
2 nums
---------------------------------------------------------------------------
TypeError |
Traceback (most recent call last) |
|
<ipython-input-55-17e46652bb1b> in <module> |
||
----> 1 |
nums.extend(6) # случай 1: один элемент не добавится |
|
2 |
nums |
|
TypeError: 'int' object is not iterable
In [56]:
1 nums.append([2, 4]) # случай 2: добавится целый список, прямо в квадратных скобках
2 nums
Out[56]:
[1, 5, 8, 9, 10, 12, 13, 6, [2, 4]]
In [57]:
1 nums[8]
Out[57]:
[2, 4]
Сразу отметим важную деталь: при работе со списками не нужно лишний раз ставить квадратные
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
5/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
скобки. Да, они используются для создания списков, но если объект уже является списком, еще одни скобки будут неуместны. Другими словами, объекты age и [age] ‒ совершенно разные вещи!
In [58]:
1 age
Out[58]:
[99, 25, 32, 48, 19]
In [15]:
1 |
print(age) |
2 |
print([age]) |
[99, 25, 32, 48, 19] [[99, 25, 32, 48, 19]]
Здесь [age] ‒ это список списков. Такой объект тоже иногда бывает полезен, но просто так создавать его не нужно. Из объекта [age] выбрать элемент с индексом 2 уже не получится:
In [60]:
1 [age][0]
Out[60]:
[99, 25, 32, 48, 19]
Придется сначала доставать первый (и единственный) элемент из [age] , а потом внутри него выбирать элемент с индексом 2.
In [63]:
1 [age][0][3]
Out[63]:
48
Другой способ добавлять элементы в список ‒ склеивать их, то есть использовать операцию, которая называется конкатенацией. В этом смысле списки очень похожи на строки, и для их конкатенации тоже используется знак + :
In [23]:
1 [1, 2, 3] + [9, 10]
Out[23]:
[1, 2, 3, 9, 10]
Запись через + кажется очень интуитивной и заманчивой, но не стоит ей часто пользоваться, особенно, когда списки большие и когда списков много. При такой конкатенации списков происходит создание нового списка, который "склеивается" из отдельных частей, чего не происходит при использовании
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
6/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
extend : там элементы просто дописываются в уже существующий список. Поэтому приписывание одного списка в конец другого быстрее и эффективнее делать именно через extend .
Для примера сравним результаты. Создадим три списка, объединим их двумя способами и зафиксируем время, за которое это объединение произойдет.
In [64]:
1 l1 = [1, 2, 3] 2 l2 = [2, 6, 7] 3 l3 = [0, 3, 6]
In [65]:
1 %%time
2
3 # % time - одно из магических слов Jupyter (magic)
4 # замеряет время исполнения ячейки с кодом
5
6 l1 + l2 + l3
Wall time: 0 ns
Out[65]:
[1, 2, 3, 2, 6, 7, 0, 3, 6]
In [66]:
1 %%time
2
3 l1.extend(l2)
4 l1.extend(l3)
5 l1
Wall time: 0 ns
Out[66]:
[1, 2, 3, 2, 6, 7, 0, 3, 6]
Кажется, что разница совсем небольшая, но не стоит забывать, что этот пример игрушечный, три списка из трех однозначных чисел.
Срезы (slices)
Мы уже познакомились с тем, как выбирать отдельные элементы из списка, однако мы еще не обсудили, как выбирать несколько элементов подряд. Такие части списков называются срезами (slices). Индексы элементов, которые должны войти в срез, указываются в квадратных скобках, через двоеточие ( начало
: конец ).
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
7/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
In [67]:
1 print(age)
[99, 25, 32, 48, 19]
In [76]:
1 age[0:3] # левый конец включается, а правый нет
2
Out[76]:
[99, 25, 32]
Важно: правый конец не включается в срез! В срез выше вошли элементы с индексами 1 и 2, элемент с индексом 3 включен не был.
Если мы хотим задать только начало или конец среза, один из индексов легко можно опустить:
In [77]:
1 print(age)
2
3 print(age[1:])
4 print(age[3:])
5 print(age[:3])
[99, |
25, |
32, |
48, |
19] |
[25, |
32, |
48, |
19] |
|
[48, |
19] |
|
|
|
[99, |
25, |
32] |
|
|
Тут мы подходим к тому, почему (http://python-history.blogspot.ru/2013/10/why-python-uses-0-based- indexing.html) нумерация элементов в Python начинается с нуля. В частности, для удобных срезов. Если нам нужны первые два элемента списка, нам не нужно долго думать и сдвигать номера элементов на единицу, достаточно просто написать, например, age[:2] .
Можно ли сделать срез, который будет включать в себя весь список? Легко!
In [79]:
1 age[:] # опускаем все индексы
Out[79]:
[99, 25, 32, 48, 19]
Получить пустой срез тоже дело нехитрое: нужно, чтобы индексы начала и конца совпадали.
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
8/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
In [80]:
1 age[2:2] # пустой срез
Out[80]:
[]
А теперь вопрос. У нас есть такой срез:
In [81]:
1 age[:2]
Out[81]:
[99, 25]
Какой срез к нему нужно добавить, чтобы получить целый список age ?
In [82]:
1 age[:2] + age[2:] # срез 2:
Out[82]:
[99, 25, 32, 48, 19]
И это будет верно для любого индекса , не только двойки.
Изменять элементы списка необязательно по одному, можно задействовать срезы.
In [84]:
1 print(age)
2
3 age[1:4] = [255, 26, 66] # заменим 1 и 2 элементы 4 print(age)
[99, 255, 26, 66, 19] [99, 255, 26, 66, 19]
Длина списка, на который мы заменяем срез, не обязательно должна совпадать с длиной среза. Можно взять список с большим числом элементов, тогда исходный список расширится, а можно с меньшим ‒ список сузится. Замены остальных элементов при этом не произойдет, новый срез просто "вклинится" в середину списка.
In [86]: |
|
|
|
|
|
|
|
||
1 |
|
print(age) |
|
|
|
|
|
||
|
|
|
|
|
|
||||
2 |
|
age[1:3] = [18, 32, 45, 55] |
|
||||||
3 |
|
print(age) |
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
[99, |
18, |
32, |
45, |
55, |
66, |
19] |
|
|
|
[99, |
18, |
32, |
45, |
55, |
45, |
55, |
66, |
19] |
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
9/14 |

19.09.2022, 01:58 |
3. Списки и цикл for - Jupyter Notebook |
In [90]:
1 print(age)
[99, 18, 19]
In [89]:
1 age[1:5] = [18] 2 print(age)
[99, 18, 19]
Изменение списков
На данный момент мы достаточно хорошо познакомились со списками. Но списки не так просты, как кажется. Давайте попробуем сделать следующее: скопировать один список в другой путем присваивания.
In [91]: |
|
|||
1 |
|
l1 = [1, 8, 9, 4] |
||
|
||||
2 |
|
l2 = l1 # сохранили список l1 в l2 |
||
3 |
|
|
|
|
4 |
|
print(l1) |
||
5 |
|
print(l2) |
||
|
|
|
|
|
[1, |
8, |
9, |
4] |
|
[1, |
8, |
9, |
4] |
Пока все ожидаемо. Теперь изменим элемент списка l2 с индексом 3:
In [92]:
1 l2[3] = 15
2 print(l2)
[1, 8, 9, 15]
А теперь посмотрим на список l1 .
In [93]:
1 print(l1)
[1, 8, 9, 15]
Несмотря на то, что список l1 мы не трогали, он изменился точно так же, как и список l2 ! Что произошло? На самом деле, когда мы записали l2 = l1 , мы скопировали не сам список, а ссылку на него. Другими словами, проводя аналогию с папкой и ярлыком, вместо того, чтобы создать новую папку l2 с элементами, такими же, как в l1 , мы создали ярлык l2 , который сам по себе ничего не представляет, а просто ссылается на папку l1 .
localhost:8888/notebooks/EXONTOOLS/2/Доп. занятия/3. Списки и цикл for.ipynb |
10/14 |