pyth_04_list_ops
Заметка 4. Методы списка
курса Математический практикум по Питону.
Шокуров Антон В.
shokurov.anton.v@yandex.ru
http://машинноезрение.рф
Версия 0.19

Анотация

Вводится базовые операции (методы) над важным объектом из Питона (Python) -- списоком (list). Показано как добавлять/удалять элементы и упорядочивать.

Ключевые слова: append, pop, extend, insert, del, id, copy, deepcopy, reverse и sort.

Это предварительная версия! Любые замечания приветсвуются.

Список -- основной объект питона!

Список не только хранить данные, но и подддерживает добавление новых элементов (в его конец), а также извлечение и удаление подсписков. по мимо этого упорядочивать элементы.

Редактирование списка

Добавление и извлечение элементов

In [1]:
# Введем список.
l = [5, 23, -11, 23, 44]
In [2]:
len( l ) # Размер исходного списка.
Out[2]:
5

Список на то и списк, что к нему можно добавить элемент (в его конец).

In [3]:
l.append(7) # Метод append, аргумент -- добавляемый элемент.
l # Элемент 7 добавился в конец (т.е. самый правый) списка.
Out[3]:
[5, 23, -11, 23, 44, 7]
In [4]:
len( l ) # Размер списка увеличился на 1 элемент.
Out[4]:
6

Взять элемент с конца списка. Полезно при однократной обработки элементов (обработал и забыл).

In [5]:
# Испольуется метод pop. Элемент будет возвращен как значение.
l.pop() # Возвращаются с конца, т.е. в обратном порядке от добавления.
Out[5]:
7
In [6]:
# Список будет уменьшен на один элемент, т.е. сокращен по размер.
l, len(l) # Получили исходный список.
Out[6]:
([5, 23, -11, 23, 44], 5)
In [7]:
# На самом деле извлесть элемент можно не только с конца.
l.pop(2) # Например в даном случае извлекается 2 элемент.
# Обычная версия эквивалентна pop(-1). Индексы нумеруются с 0.
Out[7]:
-11
In [8]:
l # Выведем элементы оставшегося списка.
Out[8]:
[5, 23, 23, 44]

Расширение списка

In [9]:
# Добавим два элемента?
l.append([5, 3]) # Или как?
l # Добавили, но наверно не так как хотели.
Out[9]:
[5, 23, 23, 44, [5, 3]]

К списку был добавлен элемент [5,3].

In [10]:
# Добавляет в конец текущего списка элементы из другого списка.
l.extend([23,3]) # Аргумент метода extend должен быть списом.
l
Out[10]:
[5, 23, 23, 44, [5, 3], 23, 3]
In [11]:
d = [6, -4] # Например, другой список.
l.extend( d ) # Добавляем его элементы в конец нашего списка.
l
Out[11]:
[5, 23, 23, 44, [5, 3], 23, 3, 6, -4]
In [12]:
d.extend(6) # Когда аргумент не явлется списком, то ошибка.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-12-6e3cea4c499b> in <module>
----> 1 d.extend(6) # Когда аргумент не явлется списком, то ошибка.

TypeError: 'int' object is not iterable

Вставить элементы

Вставляет элемент на заданное место и сдвигает другие (далее следующие) дальше. Фактически раздвигает элементы и освобождает себе место. Потом его занимает.

In [13]:
l.insert(2, 101) # Используется метод insert. 
l # Аргументом являются добавляемые элементы.
Out[13]:
[5, 23, 101, 23, 44, [5, 3], 23, 3, 6, -4]
In [14]:
l.insert(5, [-5, -6]) # Так не получится.
l # Точнее вряд ли то, что мы ожидали.
Out[14]:
[5, 23, 101, 23, 44, [-5, -6], [5, 3], 23, 3, 6, -4]
In [15]:
# Вставили число 9 перед первым вхождением 23.
l.insert( l.index(23), 9) # index ищет индекс элемента.
l
Out[15]:
[5, 9, 23, 101, 23, 44, [-5, -6], [5, 3], 23, 3, 6, -4]

Удаление элементов

In [16]:
l
Out[16]:
[5, 9, 23, 101, 23, 44, [-5, -6], [5, 3], 23, 3, 6, -4]
In [17]:
l[3:6]
Out[17]:
[101, 23, 44]
In [18]:
# Удаляет элемент с данным индексом (ами)
del l[3:6]
l
Out[18]:
[5, 9, 23, [-5, -6], [5, 3], 23, 3, 6, -4]
In [19]:
# Удаляем один или более разрозненных элементов.
del l[1], l[1] # Подумай почему индекс указан ожин и тот же.
l
Out[19]:
[5, [-5, -6], [5, 3], 23, 3, 6, -4]
In [20]:
# Вырезаем (удаляем) первый элемент из списка равный данному.
l.remove(23)
l
Out[20]:
[5, [-5, -6], [5, 3], 3, 6, -4]
In [21]:
dd = [2]
dd
Out[21]:
[2]
In [22]:
del dd[0] # При удалении последнего элемента сам список остается.
In [23]:
dd # Пустой, но список.
Out[23]:
[]

Чистка

In [24]:
f = [2, 7, 8]
f
Out[24]:
[2, 7, 8]
In [25]:
f.clear() # Можно все радикльно очистить.
f # Список пуст 
Out[25]:
[]
In [26]:
# Присваиваем новый объект,
f = [] # пустой список.

Упр. В чем разница с методом clean? Возможно лучше сначала весь ноутбук прочесть.

Действия над объектом-список

Арифметические операции над списком

Списки нужны для хранения данных. Если мы захотим все числа умножить на два, то возможно мы бы написали:

In [27]:
l * 2 # Хотим умножить все элементы списка на два.
Out[27]:
[5, [-5, -6], [5, 3], 3, 6, -4, 5, [-5, -6], [5, 3], 3, 6, -4]

И увидели бы результат, который скоре всего не соответсвует нашим ожиданиям. Дело в том, что список является объектом сам по себе. Поэтому он имеет свои смысловые нагрузки на операции. Например, + объединяет списки (конкатенация), точнее она ставит один список (второй) в конец другому (первому).

In [28]:
# Контакенация, один список добавили в конец к другому.
[1, -5] + [-7, 5, 18] # Получили новый список
Out[28]:
[1, -5, -7, 5, 18]
In [29]:
l + [2] # как append добавили 2 в конец списка.
Out[29]:
[5, [-5, -6], [5, 3], 3, 6, -4, 2]
In [30]:
# Операция выполнена, но не сохранена.
l # Поэтому старое значение списка l.
Out[30]:
[5, [-5, -6], [5, 3], 3, 6, -4]
In [31]:
# Операции умножение соответствует многократной конкатенации (т.е. повтору) самого себя:
[2, 5] * 3
Out[31]:
[2, 5, 2, 5, 2, 5]
In [32]:
# Только для целых чисел.
l * 2. # Для плавающей точки будет даже ошибка.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-32-26f6c6993bb5> in <module>
      1 # Только для целых чисел.
----> 2 l * 2. # Для плавающей точки будет даже ошибка.

TypeError: can't multiply sequence by non-int of type 'float'

Упр. Как циклически сдвинуть элементы списка? Последнее подразумевает, что все элементы сдвинутся на один вперед, а первый попадет в конец.

In [33]:
[3,5,6] - [6] # Списки вычетать нельзя.
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-33-8e7e9ddd96ad> in <module>
----> 1 [3,5,6] - [6] # Списки вычетать нельзя.

TypeError: unsupported operand type(s) for -: 'list' and 'list'

Бывает важным скопировать объект. Рассмотрим на примере списка какие с этим связаны нюансы.

Копирование списка

In [34]:
l
Out[34]:
[5, [-5, -6], [5, 3], 3, 6, -4]
In [35]:
d = l
d
Out[35]:
[5, [-5, -6], [5, 3], 3, 6, -4]
In [36]:
d[1] = 11
d
Out[36]:
[5, 11, [5, 3], 3, 6, -4]
In [37]:
l # Сюрприз! Значение в l тоже поменялось.
Out[37]:
[5, 11, [5, 3], 3, 6, -4]

В питоне на самом деле в переменных хранятся не значения, а ссылки на объекты. Поэтому при копировании переменной копируется ссылка. Для правильного копирования необходимо сам объект скопировать.

In [38]:
e = l.copy() # Делаем копию списка.
e
Out[38]:
[5, 11, [5, 3], 3, 6, -4]
In [39]:
e[1] = 22
e
Out[39]:
[5, 22, [5, 3], 3, 6, -4]
In [40]:
l # А так все как надо: элемент не изменился.
Out[40]:
[5, 11, [5, 3], 3, 6, -4]

Адрес объекта

In [41]:
# Возвращает уникальный идентификатор --
id(l) # handler -- объекта.
Out[41]:
140177405168008
In [42]:
# Идентификаторы при присвоение значения переменной совпадают.
id(l) == id(d) # в d ранее присвоили l.
Out[42]:
True
In [43]:
a = id(l) # Сохраним значение идентификатора до изменения.
l[2] = 5 # Изменяем содержимое списка.
b = id(l) # сохраняем идентификатора и после изменения.
l
Out[43]:
[5, 11, 5, 3, 6, -4]
In [44]:
# Изменение объектв не ведет к изменению его идентификатора.
a == b 
Out[44]:
True
In [45]:
a = id(l) # Аналогично.
l.append(-3)
b = id(l)
l
Out[45]:
[5, 11, 5, 3, 6, -4, -3]
In [46]:
a == b # Даже при добавлении элемента.
Out[46]:
True
In [47]:
print("id of id", id(d), ",", "id of e", id(e), '. Они равны:', id(d) == id(e))
# При создании копии самого объекта идентификатор изменился.
id of id 140177405168008 , id of e 140177404410376 . Они равны: False
In [48]:
t = [1, 2, 3]
q = [t] * 2
q
Out[48]:
[[1, 2, 3], [1, 2, 3]]
In [49]:
q[0][1] = 11
q
Out[49]:
[[1, 11, 3], [1, 11, 3]]

Упр. Объясни почему у списка q изменение произошло в двух местах.

Но, не все так просто

In [50]:
# Берем список с подсписком.
f = [ 5, [3, -2], [5, 8]]
g = f.copy() # Делаем копию его.
In [51]:
g[0] = 1 # Сделали изменение.
g
Out[51]:
[1, [3, -2], [5, 8]]
In [52]:
f # Все ок.
Out[52]:
[5, [3, -2], [5, 8]]
In [53]:
g[1] # Под индексом 1 у нас список.
Out[53]:
[3, -2]
In [54]:
# А теперь добавили к списку под индексом 1 элемент
g[1].append(-1) # -- число -1.
g # Все ок.
Out[54]:
[1, [3, -2, -1], [5, 8]]
In [55]:
# Посмотрим что с исходным списоком.
f # Хм....у исходного первый индекс тоже изменился... непорядок!
Out[55]:
[5, [3, -2, -1], [5, 8]]
In [56]:
from copy import deepcopy # Палочка выручалочка.
In [57]:
g = deepcopy( f )
g[1].append( -9 )
g
Out[57]:
[5, [3, -2, -1, -9], [5, 8]]
In [58]:
f # Ура! Первый индекс не изменился.
Out[58]:
[5, [3, -2, -1], [5, 8]]

Упр. Почему copy не спасает?

А равны ли?

Знак == позволяет проверить равенство объектов по сути, т.е. по смысловому значению. А не исходя из внутреннего представления объекта.

In [59]:
# Равенство, в частности, списков проверяется по содержимому.
[1, [4, 7], 7] == [1, [4, 7], 7]
Out[59]:
True
In [60]:
# Даже если числа имеют разный тип. Всегда можно тип привести.
[1, [4, 7], 7.] == [1, [4., 7], 7] # Один фиг же что 4, что 4..
Out[60]:
True
In [61]:
# Если объекты разные по сути, то и такой ответ.
[1, [4, 7], 7] == [1, [5, 7], 7] # 4 же не равно 5.
Out[61]:
False
In [62]:
a = [1, 2, 5]
b = a.copy()
a == b # Объекты равны несмотря на то, что они представлены разными списками, т.е. объектами.
Out[62]:
True
In [63]:
id(a) == id(b) # Как мы помним у объектов a и b разный handler (поэтому это и разные объекты).
Out[63]:
False

Для проверки ссылаются ли переменные на один объект используется оператор is.

In [64]:
a is b # Фактически эквиваленто id(a) == id(b).
Out[64]:
False

Оператор is очень важен для Питона!

Реорганизация

Упорядочивание

In [65]:
l = [5, -1, 6, -3, 4]
e = l.copy()
In [66]:
# Упорядочиваем (сортируем) сам список.
l.sort() # Копию не создает.
l # Изменяется сам список.
Out[66]:
[-3, -1, 4, 5, 6]
In [67]:
# Упорядочивание в обратом порядке.
e.sort( reverse = True )
e
Out[67]:
[6, 5, 4, -1, -3]

Порядок

In [68]:
e = [5, 4, 11]
ee = e.copy()
In [69]:
# Организует список в обратном направлении.
e.reverse() # Метод reverse изменяет сам список.
e
Out[69]:
[11, 4, 5]
In [70]:
# Можно и так, но будет создан новый список.
ee[::-1] # Сложный индекс полезная вещь!
Out[70]:
[11, 4, 5]

Статистика

In [71]:
f = [5, 8, 5, -1, 14, 5.0]
In [72]:
# Метод count возвращает количество раз объект встречается.
f.count(5) # Подсчитаем встречаемости числа 5.
Out[72]:
3
In [73]:
# Для отсутствующего в списке объекта.
f.count( 7 ) # Ествественно возвращается 0.
Out[73]:
0
In [74]:
# Не возбраняется искать объекты произвольной структуры.
f.count( 'a' ) # Ищем строку a.
Out[74]:
0
In [ ]: