Списки

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

In [1]:
l=[0,1,2,3,4,5,6,7,8,9]
l
Out[1]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [2]:
len(l)
Out[2]:
10
In [3]:
l[0]
Out[3]:
0
In [4]:
l[3]
Out[4]:
3
In [5]:
l[10]
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-5-e4a648ff0fa9> in <module>()
----> 1 l[10]

IndexError: list index out of range
In [6]:
l[-2]
Out[6]:
8
In [7]:
l[1:3]
Out[7]:
[1, 2]

Обратите внимание, что l[:3]+l[3:]==l.

In [8]:
l[:3]
Out[8]:
[0, 1, 2]
In [9]:
l[3:]
Out[9]:
[3, 4, 5, 6, 7, 8, 9]
In [10]:
l[3:3]
Out[10]:
[]
In [11]:
l[3:-2]
Out[11]:
[3, 4, 5, 6, 7]
In [12]:
l[:-2]
Out[12]:
[0, 1, 2, 3, 4, 5, 6, 7]

Списки являются изменяемыми объектами. Это сделано для эффективности. В списке может быть 1000000 элементов. Создавать его копию каждый раз, когда мы изменили один элемент, слишком дорого.

In [13]:
l[3]='три'
l
Out[13]:
[0, 1, 2, 'три', 4, 5, 6, 7, 8, 9]

Можно заменить какой-нибудь подсписок на новый список (в том числе другой длины).

In [14]:
l[3:3]=[0]
l
Out[14]:
[0, 1, 2, 0, 'три', 4, 5, 6, 7, 8, 9]
In [15]:
l[3:3]=[10,11,12]
l
Out[15]:
[0, 1, 2, 10, 11, 12, 0, 'три', 4, 5, 6, 7, 8, 9]
In [16]:
l[5:7]=[0,0,0,0]
l
Out[16]:
[0, 1, 2, 10, 11, 0, 0, 0, 0, 'три', 4, 5, 6, 7, 8, 9]
In [17]:
l[3:]=[]
l
Out[17]:
[0, 1, 2]
In [18]:
l[len(l):]=[3,4]
l
Out[18]:
[0, 1, 2, 3, 4]

Некоторые из этих операций могут быть записаны в другой форме.

In [19]:
l=[0,1,2,3,4,5,6,7]
del l[3]
l
Out[19]:
[0, 1, 2, 4, 5, 6, 7]
In [20]:
del l[3:5]
l
Out[20]:
[0, 1, 2, 6, 7]
In [21]:
l.insert(3,0)
l
Out[21]:
[0, 1, 2, 0, 6, 7]
In [22]:
l.append(8)
l
Out[22]:
[0, 1, 2, 0, 6, 7, 8]
In [23]:
l.extend([9,10,11])
l
Out[23]:
[0, 1, 2, 0, 6, 7, 8, 9, 10, 11]

Элементы списка могут быть разных типов.

In [24]:
l=[0,[1,2,3],'abc']
l[1][1]='x'
l
Out[24]:
[0, [1, 'x', 3], 'abc']

Когда мы пишем m=l, мы присваиваем переменной m ссылку на тот же объект, на который ссылается l. Поэтому, изменив этот объект (список) через l, мы увидим эти изменения и через m - ведь список всего один.

In [25]:
l=[0,1,2,3,4,5]
m=l
l[3]='три'
m
Out[25]:
[0, 1, 2, 'три', 4, 5]

Операция is проверяет, являются ли m и l одним и тем же объектом.

In [26]:
m is l
Out[26]:
True

Если мы хотим видоизменять m и l независимо, нужно присвоить переменной m не список l, а его копию. Тогда это будут два различных списка, просто в начальный момент они состоят из одних и тех же элементов. Для этого в питоне есть идиома: l[:] - это подсписок списка l от начала до конца, а подсписок всегда копируется.

In [27]:
m=l[:]

Теперь m и l - два независимых объекта, имеющих равные значения.

In [28]:
m is l
Out[28]:
False
In [29]:
m==l
Out[29]:
True

Их можно менять независимо.

In [30]:
l[3]=0
l
Out[30]:
[0, 1, 2, 0, 4, 5]
In [31]:
m
Out[31]:
[0, 1, 2, 'три', 4, 5]

Как и для строк, сложение списков означает конкатенацию, а умножение на целое число - повторение списка несколько раз. Операция in проверяет, содержится ли элемент в списке.

In [32]:
[0,1,2]+[3,4,5]
Out[32]:
[0, 1, 2, 3, 4, 5]
In [33]:
2*[0,1,2]
Out[33]:
[0, 1, 2, 0, 1, 2]
In [34]:
l=[0,1,2]
l+=[3,4,5]
l
Out[34]:
[0, 1, 2, 3, 4, 5]
In [35]:
2 in l
Out[35]:
True

Простейший вид цикла в питоне - это цикл по элементам списка.

In [36]:
for x in l:
    print(x)
0
1
2
3
4
5

Можно использовать цикл while. В этом примере он выполняется, пока список l не пуст. Этот цикл гораздо менее эффективен, чем предыдущий - в нём на каждом шаге меняется список l. Он тут приведён не для того, чтобы ему подражали, а просто чтобы показать синтаксис цикла while.

In [37]:
while l:
    print(l[0])
    l=l[1:]
0
1
2
3
4
5
In [38]:
l
Out[38]:
[]

Очень часто используются циклы по диапазонам целых чисел.

In [39]:
for i in range(4):
    print(i)
0
1
2
3

Функция range(n) возвращает диапазон целых чисел от 0 до $n-1$ (всего $n$ штук) в виде специального объекта range, который можно использовать в for цикле. Можно превратить этот объект в список функцией list. Но этого делать не нужно, если только такой список не нужен для проведения каких-нибудь списковых операций. Число n может быть равно 1000000. Зачем занимать память под длинный список, если он не нужен? Для написания цикла достаточно короткого объекта range, который хранит только пределы.

In [40]:
r=range(4)
r
Out[40]:
range(0, 4)
In [41]:
list(r)
Out[41]:
[0, 1, 2, 3]

Функции range можно передать первый параметр - нижний предел.

In [42]:
for i in range(2,4):
    print(i)
2
3
In [43]:
r=range(2,4)
r
Out[43]:
range(2, 4)
In [44]:
list(r)
Out[44]:
[2, 3]

Функция list превращает строку в список символов.

In [45]:
l=list('абвгд')
l
Out[45]:
['а', 'б', 'в', 'г', 'д']

Как написать цикл, если в его теле нужно использовать и номера элементов списка, и сами эти элементы? Первая идея, которая приходит в голову по аналогии с C - это использовать range.

In [46]:
for i in range(len(l)):
    print(i,'  ',l[i])
0    а
1    б
2    в
3    г
4    д

Можно поступить наоборот - устроить цикл по элементам списка, а индексы вычислять.

In [47]:
i=0
for x in l:
    print(i,'  ',x)
    i+=1
0    а
1    б
2    в
3    г
4    д

Оба этих способа не есть идиоматический питон. Более изящно использовать функцию enumerate, которая на каждом шаге возвращает пару из индекса i и i-го элемента списка.

In [48]:
for i,x in enumerate(l):
    print(i,'  ',x)
0    а
1    б
2    в
3    г
4    д

Про такие пары мы поговорим в следующем параграфе.

Довольно часто удобно использовать цикл while True:, то есть пока рак на горе не свистнет, а выход (или несколько выходов) из него устраивать в нужном месте (или местах) при помощи break.

In [49]:
while True:
    print(l[-1])
    l=l[:-1]
    if l==[]:
        break
д
г
в
б
а

Этот конкретный цикл - отнюдь не пример для подражания, он просто показывает синтаксис.

Можно строить список поэлементно.

In [50]:
l=[]
for i in range(10):
    l.append(i**2)
l
Out[50]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Но более компактно и элегантно такой список можно создать при помощи генератора списка (list comprehension). К тому же это эффективнее - размер списка известен заранее, и не нужно много раз увеличивать его. Опытные питон-программисты используют генераторы списков везде, где это возможно (и разумно).

In [51]:
[i**2 for i in range(10)]
Out[51]:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
In [52]:
[[i,j] for i in range(3) for j in range(2)]
Out[52]:
[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]

В генераторе списков могут присутствовать некоторые дополнительные элементы, хотя они используются реже. Например, в список-результат можно включить не все элементы.

In [53]:
[i**2 for i in range(10) if i!=5]
Out[53]:
[0, 1, 4, 9, 16, 36, 49, 64, 81]

Создадим список случайных целых чисел.

In [54]:
from random import randint
l=[randint(0,9) for i in range(10)]
l
Out[54]:
[6, 0, 5, 8, 9, 0, 9, 4, 0, 6]

Функция sorted возвращает отсортированную копию списка. Метод sort сортирует список на месте. Им можно передать дополнительный параметр - функцию, указывающую, как сравнивать элементы.

In [55]:
sorted(l)
Out[55]:
[0, 0, 0, 4, 5, 6, 6, 8, 9, 9]
In [56]:
l
Out[56]:
[6, 0, 5, 8, 9, 0, 9, 4, 0, 6]
In [57]:
l.sort()
l
Out[57]:
[0, 0, 0, 4, 5, 6, 6, 8, 9, 9]

Аналогично, функция reversed возвращает обращённый список (точнее говоря, некий объект, который можно использовать в for цикле или превратить в список функцией list). Метод reverse обращает список на месте.

In [58]:
list(reversed(l))
Out[58]:
[9, 9, 8, 6, 6, 5, 4, 0, 0, 0]
In [59]:
l
Out[59]:
[0, 0, 0, 4, 5, 6, 6, 8, 9, 9]
In [60]:
l.reverse()
l
Out[60]:
[9, 9, 8, 6, 6, 5, 4, 0, 0, 0]

Метод split расщепляет строку в список подстрок. По умолчанию расщепление производится по пустым промежуткам (последовательностям пробелов и символов tab и newline). Но можно передать ему дополнительный аргумент - подстроку-разделитель.

In [61]:
s='abc \t def \n ghi'
l=s.split()
l
Out[61]:
['abc', 'def', 'ghi']

Чтобы напечатать элементы списка через запятую или какой-нибудь другой символ (или строку), очень полезен метод join. Он создаёт строку из всех элементов списка, разделяя их строкой-разделителем. Запрограммировать это в виде цикла было бы существенно длиннее, и такую программу было бы сложнее читать.

In [62]:
s=', '.join(l)
s
Out[62]:
'abc, def, ghi'
In [63]:
s.split(', ')
Out[63]:
['abc', 'def', 'ghi']