Еще одно ненужное руководство по Python: Списки, кортежи, словари

Список, кортеж и словарь – три основных структуры данных, которые позволяют работать с наборами данных. Список является аналогом массива в других языках, кортеж можно назвать неизменяемым массивом, а словарь похож на хеш-таблицу.

Список

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

Python 2.6.5 (r265:79063, Apr  1 2010, 05:28:39) 
[GCC 4.4.3 20100316 (prerelease)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> x = [4, 5, 2, "Hello!", 2.71878]
>>> type(x)
<type 'list'>

Ранее говорилось, что питон – язык с динамической типизацией. Так что и в списке мы можем хранить значения разных типов, в отличие от массивов в С (не говорите мне про void*). Можно, в том числе, хранить в списке и другие списки, что будет аналогом матрицы, кубического массива и т.п.

Получить элемент в массиве просто:

>>> x[0]
4
>>> x[3]
'Hello!'

Изменить тоже просто:

>>> x[0] = 123
>>> x[0]
123

Наверное, вам так и хочется вписать какой-нибудь большой индекс и посмотреть, что же выдаст питон. Нет, он не выдаст вам кусок памяти из соседнего массива:

>>> x[100500]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Найти дырку в питоне не удалось, он выдал нам ошибку, мол, индекс списка корявый. Про обработку исключительных ситуаций будет рассказано в одной из следующих статей.

А вот с отрицательными индексами это не произойдет:

>>> x[-1]
2.7187800000000002
>>> x[-2]
'Hello!'

Нет, это не баг. Это фича. Если индекс меньше нуля, то питон берет нужный вам элемент с конца списка. Правда, я вам немного соврал, когда говорил, что с отрицательными индексами питон не выдаст ошибку:

>>> x[-100500]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

Но я думаю, вы и без меня бы догадались, что питон не выйдет за границы списка.

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

>>> my_list = [1, 2, 3, 4, 5, 6]
>>> my_list.append(7)
>>> my_list
[1, 2, 3, 4, 5, 6, 7]
>>> my_list.remove(3)
>>> my_list
[1, 2, 4, 5, 6, 7]

Есть также поиск (метод index), вставка в произвольное место (insert)… Подробности, как всегда, во встроенной справке, а здесь показательные выступления:

>>> my_list
[1, 2, 4, 5, 6, 7]
>>> my_list.append(2)
>>> my_list.count(2)
2
>>> my_list.count(4)
1
>>> my_list.extend([-1, -2, -3])
>>> my_list
[1, 2, 4, 5, 6, 7, 2, -1, -2, -3]
>>> my_list.index(4)
2
>>> my_list.index(100500)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.index(x): x not in list
>>> my_list.insert(0, 123)
>>> my_list
[123, 1, 2, 4, 5, 6, 7, 2, -1, -2, -3]
>>> my_list.pop()
-3
>>> my_list
[123, 1, 2, 4, 5, 6, 7, 2, -1, -2]
>>> my_list.remove(-1)
>>> my_list
[123, 1, 2, 4, 5, 6, 7, 2, -2]
>>> my_list.reverse()
>>> my_list
[-2, 2, 7, 6, 5, 4, 2, 1, 123]
>>> my_list.sort()
>>> my_list
[-2, 1, 2, 2, 4, 5, 6, 7, 123]

Срезы

Невероятно полезной возможностью питона является удобный синтаксис для срезов (slice). PHP’шники уже, наверное, догадались, о чем пойдет речь:

>>> x = [123, 5, 2, "Hello!", 2.71878]
>>> x[1:4]
[5, 2, 'Hello!']

Да, таким простым способом мы получили часть списка. Можно получить часть списка от начала до определенной точки:

>>> x[:3]
[123, 5, 2]

Также можно получить часть списка от определенной точки до конца:

>>> x[3:]
['Hello!', 2.7187800000000002]

А вот получить список от начала до конца таким образом не удастся, но оно особо и не надо. Ну или если оно особо надо, то используйте конструктор копирования:

>>> x
[123, 5, 2, 'Hello!', 2.7187800000000002]
>>> y = x
>>> z = list(x)
>>> x[0] = 4
>>> x
[4, 5, 2, 'Hello!', 2.7187800000000002]
>>> y
[4, 5, 2, 'Hello!', 2.7187800000000002]
>>> z
[123, 5, 2, 'Hello!', 2.7187800000000002]

Кстати, вы заметили, что список y не изменился? Потому что в строке y = x создается не копия объекта x, а ссылка на него, и помещается она в y. Такой механизм действует для всех объектов в питоне, кроме чисел, булевых переменных и None. Для строк тоже не действует. Прямо как в Java.

Знатоки функциональных языков уже должны были догадаться, что аналогом head является x[0], а аналогом tail – x[1:]. Ну ничего, позже я расскажу и о лямбда-функциях, и о свертках, а сейчас будут…

List comprehensions

Дословно это переводится как “список охвата”. Или “список включений”… В общем, я не встречал где-либо перевода для этого термина.

List comprehensions позволяют легко построить новый список на основе текущего. Аналог map и filter в функциональных языках (map с filter таки и тут есть, если интересно, то help(map) или help(filter)).

>>> numbers = range(1, 10)
>>> numbers
[1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> squares = [ x * x for x in numbers ]
>>> squares
[1, 4, 9, 16, 25, 36, 49, 64, 81]

Да, здесь мы сделали некое действие над всеми элементами списка и занесли результат в новый список.

Можно также отсеивать элементы в списке:

>>> evens = [ x for x in numbers if x % 2 == 0 ]
>>> evens
[2, 4, 6, 8]

Ну и разумеется эти действия можно совмещать:

>>> from math import sqrt
>>> [ sqrt(i) for i in [-2, 4, -17, 9, 2] if i >= 0 ]
[2.0, 3.0, 1.4142135623730951]

Кортежи

Или tuple. Википедия нам говорит, что это последовательность конечного числа элементов. И она не врет.

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

>>> x = (1, 2, 3)
>>> x
(1, 2, 3)
>>> x[0]
1
>>> x[0] = 123
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> x.append(123)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute 'append'

Обратите внимание, если вы хотите создать кортеж из одного элемента, то требуется поставить одну лишнюю запятую, иначе питон просто раскроет скобки:

>>> a = (False)
>>> a
False
>>> type(a)
<type 'bool'>
>>> b = (True, )
>>> b
(True,)
>>> type(b)
<type 'tuple'>

Из публичных методов объекта-кортежа есть только count и index, которые работают также, как в списке.

Кортеж можно разбить на составные части еще одним простым способом:

>>> foo, bar, lol = (5, 7, 9)
>>> foo
5
>>> bar, lol
(7, 9)

Это очень удобный способ задать значения сразу нескольким переменным. Можно даже без скобок:

>>> x1, x2 = -1, 1

Словари

Словарь – структура, которая хранит данные в форме “ключ: значение”. Аналог HashMap в Java. Используются фигурные скобки.

>>> d = {'first': True, 'second': False, 1: 123, 3.14: 456}
>>> d
{1: 123, 'second': False, 3.1400000000000001: 456, 'first': True}
>>> type(d)
<type 'dict'>
>>> d[1]
123
>>> d["1"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: '1'
>>> d["second"]
False

Напоминает JSON, правда?

Чтобы добавить элемент в словарь никакой append не нужен:

>>> d["hello"] = "world"
>>> d
{1: 123, 'second': False, 3.1400000000000001: 456, 'hello': 'world', 'first': True}

Можно легко получить список всех ключей, значений, или их кортежей:

>>> d.keys()
[1, 'second', 3.1400000000000001, 'hello', 'first']
>>> d.values()
[123, False, 456, 'world', True]
>>> d.items()
[(1, 123), ('second', False), (3.1400000000000001, 456), ('hello', 'world'), ('first', True)]

Есть также методы iterkeys, itervalues, iteritems, которые являются генераторами (как и упомянутый в прошлый раз xrange). А о генераторах речь пойдет нескоро.

Вот так просто можно пройти по словарю в цикле:

>>> en_ru = {'apple': 'яблоко', 'potato': 'картофель', 'tomato': 'томат', 'orange': 'апельсин'}
>>> for english in en_ru.keys():
... 	russian = en_ru[english]
... 	print english, russian
... 
tomato томат
orange апельсин
apple яблоко
potato картофель

Или еще проще:

>>> for english, russian in en_ru.items():
... 	print english, russian
... 
tomato томат
orange апельсин
apple яблоко
potato картофель

Ну и еще одно показательное выступление:

>>> d
{1: 123, 'second': False, 3.1400000000000001: 456, 'hello': 'world', 'first': True}
>>> d.clear()
>>> d
{}
>>> d = {'a': 'apple', 'b': 'bananna', 'c': 'cherry'}
>>> d.copy()
{'a': 'apple', 'c': 'cherry', 'b': 'bananna'}
>>> d.has_key('a')
True
>>> d.has_key('z')
False

Выводы

В ходе выполнения лабораторной работы мы изучили структу Такие структуры есть во многих языках программирования, но, возможно, в питоне ими удобнее пользоваться, чем в других. А возможно и нет. На вкус и цвет фломастеры разные.