Введение в программирование на Python

Кортежи (tuples)

Показывать лекцию целиком

По ссылке youtube выложено видео с русскими титрами.

20.1. Кортежи неизменяемы

Кортеж Интересный факт: слово tuple — "кортеж" – произошло от названий последовательностей чисел различной длины: двухэлементных, трехэлементных, четырехэлементных, пяти-, шести- и т.д. (double, triple, quadruple, quituple, sextuple, …). – это последовательность значений, аналогичная списку. Значения, хранимые в кортеже, могут быть различных типов; они индексируются целыми числами. Важное отличие от списков – кортежи неизменяемы.

Также кортежи сравниваются и хешируются, что позволяет сортировать их списки и использовать кортежи в качестве ключевых значений в словарях Питона. Синтаксически, кортеж – это список значений, разделенных запятыми:

>>> t = 'a', 'b', 'c', 'd', 'e'

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

>>> t = ('a', 'b', 'c', 'd', 'e')

При создании кортежа с одним элементом необходимо в конце поставить запятую:

>>> t1 = ('a',)
>>> type(t1)
<type 'tuple'>
  

Без запятой в конце Питон распознает ('a') как выражение в скобках, которое является строкой:

>>> t2 = ('a')
>>> type(t2)
<type 'str'>
  

Другой способ образования кортежа – с помощью встроенной функции tuple. При вызове без аргумента она создает пустой кортеж:

>>> t = tuple()
>>> print t
()
  

Если аргумент является последовательностью (строкой, списком или кортежем), то результатом вызова функции tuple станет кортеж с последовательностью элементов:

>>> t = tuple('lupins')
>>> print t
('l', 'u', 'p', 'i', 'n', 's')
  

Так как tuple является именем конструктора, следует избегать использования этого слова в качестве переменной.

Большинство операторов списка также работают и с кортежами.

Оператор квадратные скобки индексирует элемент:

>>> t = ('a', 'b', 'c', 'd', 'e')
>>> print t[0]
'a'
  

Оператор двоеточие задает диапазон элементов:

>>> print t[1:3]
('b', 'c')
  

При попытке изменить один из элементов кортежа выдается сообщение об ошибке:

>>> t[0] = 'A'
TypeError: object doesn't support item assignment 
(Ошибка Типа:  объект не допускает изменения его элементов)
  

Нельзя изменять элементы кортежа, можно лишь заменить один кортеж на другой:

>>> t = ('A',) + t[1:]
>>> print t
('A', 'b', 'c', 'd', 'e')
  

20.2. Сравнение кортежей

Операторы сравнения работают как с кортежами, так и с другими последовательностями: Питон сначала сравнивает первые элементы последовательностей; если они равны, сравниваются следующие элементы и так до тех пор, пока не найдутся неравные элементы. Последующие элементы не рассматриваются (даже если они очень большие).

>>> (0, 1, 2) < (0, 3, 4)
True
>>> (0, 1, 2000000) < (0, 3, 4)
True
  

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

Эта характерная особенность сортировки легла в основу метода под названием DSU (Decorate-Sort-Undecorate): разметка (Decorate) последовательности с помощью создания списка кортежей, каждый из которых включает элемент исходной последовательности плюс один или несколько вспомогательных ключей сортировки, предшествующих этому элементу; затем сортировка (Sort) списка кортежей с помощью встроенной функции Питона и далее удаление разметки (Undecorate) путем извлечения отсортированных элементов исходной последовательности.

Например, предположим, что есть список слов, которые необходимо отсортировать по их длине – от самого длинного к самому короткому:

def sort_by_length(words):
t = list()
for word in words:
t.append((len(word), word))
t.sort(reverse=True)
res = list()
for length, word in t:
res.append(word)
return res
  

Первый цикл создаст список кортежей, где каждый кортеж – это слово с определенной длиной.

При сортировке сравниваются первые элементы — длины слов, вторые элементы рассматриваются лишь при равенстве первых. Ключевой аргумент reverse=True предписывает выполнять сортировку в порядке убывания.

Второй цикл проходит список кортежей и формирует список слов в порядке убывания по длине.

20.3. Присваивание кортежей

Одной из особенностей синтаксиса языка Питон является возможность размещать кортеж слева от оператора присваивания. Это позволяет за один раз присваивать значение более чем одной переменной.

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

>>> m = ( 'have', 'fun' )
>>> x, y = m
>>> x
'have'
>>> y
'fun'
>>>
  

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

>>> m = ( 'have', 'fun' )
>>> x = m[0]
>>> y = m[1]
>>> x
'have'
>>> y
'fun'
>>>
  

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

>>> m = ( 'have', 'fun' )
>>> (x, y) = m
>>> x
'have'
>>> y
'fun'
>>>
  

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

>>> a, b = b, a
  

Обе части этого оператора – кортежи, но слева – кортеж переменных, а справа – кортеж выражений. Каждое значение справа присваивается соответствующей переменной слева. Все выражения справа вычисляются до любых присваиваний.

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

>>> a, b = 1, 2, 3
ValueError: too many values to unpack
(Ошибка Значений: слишком много значений для распаковки)
  

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

>>> addr = 'monty@python.org'
>>> uname, domain = addr.split('@')
  

Возвращаемое значение метода split является списком из двух элементов; первый элемент присваивается переменной uname, второй — переменной domain.

>>> print uname
monty
>>> print domain
python.org

20.4. Словари и кортежи

У словарей есть метод с названием "items", который возвращает список кортежей, где каждый кортеж – это пара ключ- значение Версия Питона 3.0 в данном случае работает немного иначе. :

>>> d = {'a':10, 'b':1, 'c':22}
>>> t = d.items()
>>> print t
[('a', 10), ('c', 22), ('b', 1)]
  

Как и следовало ожидать, в словаре элементы располагаются в произвольном порядке. Но, поскольку список кортежей является списком и кортежи можно сравнивать — можно сортировать список кортежей. Преобразование словаря в список кортежей – это способ вывода содержимого словаря, отсортированного по ключу:

>>> d = {'a':10, 'b':1, 'c':22}
>>> t = d.items()
>>> t
[('a', 10), ('c', 22), ('b', 1)]
>>> t.sort()
>>> t
[('a', 10), ('b', 1), ('c', 22)]
  

Новый список сортируется в алфавитном порядке по значению ключа.

20.5. Множественное присваивание при работе со словарями

Сочетая использование функции items, присваивание кортежей и цикл for, можно написать изящный фрагмент кода для перечисления ключей и их значений в словаре в одном цикле:

for key, val in d.items():
print val, key
  

В этом цикле используются две итерационные переменные, потому что функция items возвращает список кортежей; пара переменных key, val также образует кортеж, который пробегает все пары ключ/значение, содержащиеся в словаре.

После каждой итерации переменные key и val переходят к следующей паре ключ/значение, содержащейся в словаре (в порядке, задаваемой хеш-функцией).

На выходе цикла получим:

0 a
2 c
1 b
  

Значения напечатаны в порядке, задаваемом хеш-функцией (т.е. без определенной упорядоченности).

Сочетая два указанных метода, мы можем вывести содержание словаря, которое отсортировано по значениям, содержащимся в парах ключ/значение.

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

Когда список кортежей (значение, ключ) создан, несложно отсортировать его в обратном порядке и напечатать новый отсортированный список.

>>> d = {'a':10, 'b':1, 'c':22}
>>> l = list()
>>> for key, val in d.items() :
... l.append( (val, key) )
...
>>> l
[(10, 'a'), (22, 'c'), (1, 'b')]
>>> l.sort(reverse=True)
>>> l
[(22, 'c'), (10, 'a'), (1, 'b')]
>>>
  

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

20.6. Наиболее часто встречающиеся слова

Вернемся назад к нашему примеру – отрывку текста из произведения "Ромео и Джульетта", действие 2, сцена 2. Мы можем дополнить нашу программу, если применим технику вывода десяти наиболее распространенных в тексте слов, используя следующий код:

import string
fhand = open('romeo-full.txt')
counts = dict()

for line in fhand:

line = line.translate(None, string.punctuation)
line = line.lower()
words = line.split()

for word in words:
if word not in counts:

counts[word] = 1
else:
counts[word] += 1
# Sort the dictionary by value
lst = list()
for key, val in counts.items():
lst.append( (val, key) )
lst.sort(reverse=True)
for key, val in lst[:10] :
print key, val
  

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

Поскольку значение ключа является первым элементом кортежа, оно будет использоваться первым для сравнения; если кортежей с подобным значением несколько, то будут сравниваться вторые элементы кортежей, т.е. ключи. Таким образом, кортежи с одинаковыми значениями будут сортироваться по ключам в алфавитном порядке.

В конце мы запишем изящный цикл for, который выполняет множественное присваивание в каждой итерации и выводит десять наиболее распространенных слов с помощью перебора части списка (lst[:10]).

Вывод наконец-то выглядит так, как это требуется для анализа частоты слов.

61 i
42 and
40 romeo
34 to
34 the
32 thou
32 juliet
30 that
29 my
24 thee
  

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

20.7. Использование кортежей в качестве ключей в словарях

Кортежи, в отличие от списков, хешируемы, поэтому, если мы хотим создать составной ключ для использования в словаре, нужно использовать кортеж в качестве ключа.

Например, нам понадобится составной ключ, если мы захотим создать телефонный справочник, который отображает пары (фамилия, имя) на телефонные номера. Предположим, что переменные last (фамилия – last name), first (имя – first name) и number (номер) определены, тогда мы можем внести запись в справочник следующим образом:

directory[last,first] = number

Выражение в скобках является кортежем. Мы можем использовать присваивание кортежей в цикле for для печати содержимого справочника.

for last, first in directory:
print first, last, directory[last,first]
  

Цикл перебирает ключи справочника, которые являются кортежами. Происходит присваивание элементов каждого кортежа переменным last и first, затем выводится имя и соответствующий телефонный номер.

20.8. Последовательности: строки, списки, кортежи...

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

Во многих случаях различные виды последовательностей (строки, списки, кортежи) могут использоваться как взаимозаменяемые. Но как выбрать конкретный тип среди всех возможных? Начнем с очевидного: строки ограничены тем, что их элементами являются символы. К тому же строки неизменяемы. Если необходима возможность изменения символов в строке (а не просто создание новой строки), можно использовать вместо строк списки символов.

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

  • В некоторых случаях, например, в операторе return, синтаксически проще создать кортеж, чем список. В других случаях можно предпочесть список.
  • Если мы хотим использовать последовательность как ключ в словаре, нам обязательно нужен неизменяемый тип, например, кортеж или строка.
  • Если последовательность используется в качестве аргумента функции, применение кортежей уменьшает вероятность незапланированного поведения благодаря ссылкам.
  • Так как кортежи неизменяемы, они не позволяют применять такие методы, как sort и reverse, которые изменяют содержимое списка. Однако Питон предоставляет встроенные функции sorted и reversed (сортировка и обращение), которые принимают любую последовательность в качестве параметра и возвращают новый список с теми же элементами в другом порядке.

    20.9. Отладка

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

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

    При отладке программы и особенно при исправлении тяжелой ошибки пытайтесь выполнить четыре вещи.

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

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

    Размышляйте: найдите время на размышления! Что это за ошибка: синтаксическая, времени выполнения, семантическая? Какую информацию можно получить из сообщений об ошибках или из вывода программы? Какая ошибка может породить эту проблему? Какие последние изменения были внесены до появления ошибки?

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

    Начинающие программисты часто застревают на одном из этих действий и забывают о других.

    Каждое действие имеет свои собственные ошибки. Например, чтение кода позволит обнаружить опечатку, но не поможет, если ошибка заключается в концептуальном непонимании. Если вы не понимаете, что делает ваша программа, вы не сможете обнаружить ошибку, даже если прочтете код 100 раз, потому что ошибка у вас в голове. Экспериментирование может помочь, особенно при запуске небольших простых тестов. Но если вы начнете экспериментировать без обдумывания и чтения кода, то в конце концов можете дойти до "программирования методом случайного блуждания", которое представляет собой процесс внесения случайных изменений до тех пор, пока программа не станет работать правильно. Излишне говорить, что случайное блуждание может длиться неопределенно долго. Не лучше ли немного подумать?

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

    Небольшой перерыв помогает дальнейшим размышлениям. Также полезны обсуждения. Если вы объясните проблему кому-то (или даже себе), возможно, вы найдете ответ еще до того, как закончите задавать вопрос.

    Однако даже лучшие способы отладки бессильны, когда в коде слишком много ошибок или когда код, который вы хотите исправить, слишком большой и сложный. Иногда лучше отступить и упростить программу, пока не получится то, что работает и всё еще находится под вашим контролем.

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

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

    20.10. Глоссарий

    Сравнимый (comparable): тип, два значения которого можно сравнивать друг с другом, определяя, что первое значение больше, меньше или равно второму. Сравнимые типы можно помещать в список и сортировать.

    Структура данных (data structure): совокупность связанных значений, часто представленная в виде списков, словарей, кортежей и т.д.

    DSU: Сокращение от "decorate-sort-undecorate," (декорирование-сортировка-раздекорирование) – метод, который включает в себя построение списка кортежей, сортировку и извлечение части результата.

    Сборка (gather): операция формирования кортежа путем объединения произвольного числа аргументов.

    Хешируемый (hashable): тип, имеющий хеш-функцию. Неизменяемые типы, такие как целые или вещественные числа и строки, являются хешируемыми; изменяемые, такие как списки и словари, – нет.

    Разброс (scatter): операция использования последовательности в качестве списка аргументов.

    Форма структуры данных (shape): совокупность типа, размера и состава структуры данных.

    Одноэлементный (singleton): список (или другая последовательность) с одним элементом.

    Кортеж (tuple): неизменяемая последовательность элементов.

    Присваивание кортежей (tuple assignment): присваивание последовательности, стоящей с правой стороны от оператора присваивания, кортежу переменных с левой стороны. Правая сторона сначала вычисляется, затем вычисленные значения присваиваются переменным с левой стороны.

    20.11. Упражнения

    Упражнение 20.1.

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

    После завершения обработки данных выводится имя человека, отправившего наибольшее число сообщений, при помощи создания списка кортежей (количество, e-mail) из словаря и последующей сортировки списка в обратном порядке.

    Пример:

    From stephen.marquard@uct.ac.za Sat Jan 5 09:14:16 2008
    Enter a file name: mbox-short.txt
    cwen@iupui.edu 5
    Enter a file name: mbox.txt
    zqian@umich.edu 195
      

    Упражнение 20.2.

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

    Пример выполнения программы:

    python timeofday.py
    Enter a file name: mbox-short.txt
    
    04 3
    06 1
    07 1
    09 2
    10 3
    11 6
    14 1
    15 2
    16 4
    17 2
    18 1
    19 1
      

    Упражнение 20.3.

    Напишите функцию most_frequent (наиболее частые), которая получает строку и выводит ее буквы в порядке убывания частоты. Найдите образцы текста на разных языках и посмотрите, как частоты букв меняются в разных языках. Сравните свои результаты с таблицей по адресу wikipedia.org/wiki/Letter_frequencies.

    Вернуться к учебному плану