Глава 2. Встроенные типы данных

Добавлено 3 апреля 2020 в 21:18

В начале всяческой философии лежит удивление, изучение движет его вперёд, невежество убивает его.

Мишель де Монтень

Содержание главы

Погружение

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

В Python имеется множество встроенных типов данных. Вот наиболее важные из них:

  1. Логический, может принимать одно из двух значений – True (истина) или False (ложь).
  2. Числа, могут быть целыми (1 и 2), с плавающей точкой (1.1 и 1.2), дробными (1/2 и 2/3), и даже комплексными.
  3. Строки – последовательности символов Юникода, например, HTML-документ.
  4. Байты и массивы байтов, например, файл изображения в формате JPEG.
  5. Списки – упорядоченные последовательности значений.
  6. Кортежи – упорядоченные неизменяемые последовательности значений.
  7. Множества – неупорядоченные наборы значений.
  8. Словари – неупорядоченные наборы пар вида ключ-значение.

Конечно, существуют и многие другие типы данных. В языке Python всё является объектом, поэтому в нём имеются также и такие типы, как модуль, функция, класс, метод, файл, и даже скомпилированный код. Некоторые из них вы уже встречали: у модулей есть имена, функции имеют строки документации, и т. д. С классами вы познакомитесь в главе «Классы и итераторы»; с файлами – в главе «Файлы».

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

2.2 Логические значения

Практически любое выражение можно использовать в логическом контексте.

Логический тип данных может принимать одно из двух значений: истина или ложь. В Python имеются две константы с понятными именами True (от англ. true – истина) и False (от англ. false – ложь), которые можно использовать для непосредственного присвоения логических значений. Результатом вычисления выражений также может быть логическое значение. В определенных местах (например, в операторе if), Python ожидает, что результатом вычисления выражения будет логическое значение. Такие места называют логическим контекстом. Практически любое выражение можно использовать в логическом контексте, Python в любом случае попытается определить его истинность. Для этого имеются отдельные наборы правил, для различных типов данных, указывающие на то, какие из их значений считать в логическом контексте истинными, а какие ложными. (Эта идея станет более понятна по мере ознакомления с конкретными примерами далее в этой главе.)

К примеру, рассмотрим следующий отрывок из программы humansize.py:

if size < 0:
    raise ValueError('число должно быть неотрицательным')

Здесь переменная size и значение 0 имеют тип целого числа, а знак < между ними является числовым оператором. Результатом же вычисления выражения size < 0 всегда будет логическое значение. Вы можете самостоятельно в этом убедиться с помощью интерактивной оболочки Python:

>>> size = 1
>>> size < 0
False
>>> size = 0
>>> size < 0
False
>>> size = -1
>>> size < 0
True

Из-за некоторых обстоятельств, связанных с наследием оставшимся от Python 2, логические значения могут трактоваться как числа. True как 1, и False как 0.

>>> True + True
2
>>> True - False
1
>>> True * False
0
>>> True / False
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: int division or modulo by zero

Перевод сообщения оболочки

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: int division or modulo by zero
Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, <модуль>
ZeroDivisionError: целочисленное деление на ноль или остаток по модулю ноль

Ой-ой-ой! Не делайте так! Забудьте даже, что я упоминал об этом.

2.3 Числа

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

>>> type(1)                 ①
<class 'int'>
>>> isinstance(1, int)      ②
True
>>> 1 + 1                   ③
2
>>> 1 + 1.0                 ④
2.0
>>> type(2.0)
<class 'float'>
  1. Строка 1. Можно использовать функцию type() для проверки типа любого значения или переменной. Как и ожидалось, число 1 имеет тип int (целое).
  2. Строка 3. Функцию isinstance() тоже можно использовать для проверки принадлежности значения или переменной определенному типу.
  3. Строка 5. Сложение двух значений типа int дает в результате тот же int.
  4. Строка 7. Сложение значений типа int и float дает в результате float. Для выполнения операции сложения Python преобразует значение типа int в значение типа float, и в результате возвращает float.

2.3.1 Преобразование целых чисел в десятичные дроби и наоборот

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

>>> float(2)                ①
2.0
>>> int(2.0)                ②
2
>>> int(2.5)                ③
2
>>> int(-2.5)               ④
-2
>>> 1.12345678901234567890  ⑤
1.1234567890123457
>>> type(1000000000000000)  ⑥
<class 'int'>
  1. Строка 1. Можно явно преобразовать значение типа int в тип float, вызвав функцию float().
  2. Строка 3. Так же нет ничего удивительного в том, что можно преобразовать значение типа float в значение типа int, с помощью функции int().
  3. Строка 5. Функция int() отбрасывает дробную часть числа, а не округляет его.
  4. Строка 7. Функция int() «округляет» отрицательные числа в сторону увеличения. Она не возвращает целую часть числа, как делает функция floor(), а просто отбрасывает дробную часть.
  5. Строка 9. Точность чисел с плавающей точкой равна 15 десятичным знакам в дробной части.
  6. Строка 11. Целые числа могут быть сколь угодно большими.

Python 2 имел отдельные типы целых чисел: int и long. Тип int был ограничен значением sys.maxint, которое менялось в зависимости от платформы, но обычно было равно 232−1. Python 3 же имеет только один целочисленный тип, который в большинстве случаев ведёт себя как тип long в Python 2. Подробности смотрите в PEP 237.

2.3.2 Основные операции с числами

Над числами можно выполнять различные операции.

>>> 11 / 2      ①
5.5
>>> 11 // 2     ②
5
>>> −11 // 2    ③
−6
>>> 11.0 // 2   ④
5.0
>>> 11 ** 2     ⑤
121
>>> 11 % 2      ⑥
1
  1. Строка 1. Оператор / выполняет деление чисел с плавающей точкой. Он возвращает значение типа float, даже если и делимое, и делитель, имеют тип int.
  2. Строка 3. Оператор // выполняет целочисленное деление необычного вида. Когда результат положительный, можете считать, что он просто отбрасывает (не округляет) дробную часть, но будьте осторожны с этим.
  3. Строка 5. Когда выполняется целочисленное деление отрицательных чисел, оператор // округляет результат до ближайшего целого в «большую» сторону. С математической точки зрения, это конечно же округление в меньшую сторону, т. к. −6 меньше чем −5; но это может сбить вас с толку, и вы будете ожидать, что результат будет «округлён» до −5.
  4. Строка 7. Оператор // не всегда возвращает целое число. Если хотя бы один из операндов (делимое или делитель) будет типа float, то хотя результат и будет округлён до ближайшего целого, в действительности он также будет иметь тип float.
  5. Строка 9. Оператор ** выполняет возведение в степень. 112 равно 121.
  6. Строка 11. Оператор % возвращает остаток от целочисленного деления. 11, делённое на 2, даёт 5 и 1 в остатке, поэтому здесь результат равен 1.

В Python 2, оператор / обычно означает целочисленное деление, но добавив в код специальную директиву можно заставить его выполнять деление с плавающей точкой. В Python 3, оператор / всегда означает деление с плавающей точкой. Подробности смотрите в PEP 238.

2.3.3 Дроби

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

>>> import fractions              ①
>>> x = fractions.Fraction(1, 3)  ②
>>> x
Fraction(1, 3)
>>> x * 2                         ③
Fraction(2, 3)
>>> fractions.Fraction(6, 4)      ④
Fraction(3, 2)
>>> fractions.Fraction(0, 0)      ⑤
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fractions.py", line 96, in __new__
    raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)

Перевод сообщения оболочки

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "fractions.py", line 96, in __new__
    raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)
Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, в <модуль>
  Файл "fractions.py", строка 96, в __new__
    raise ZeroDivisionError('Дробь(%s, 0)' % числитель)
ZeroDivisionError: Дробь(0, 0)
  1. Строка 1. Перед началом использования дробей, импортируйте модуль fractions.
  2. Строка 2. Чтобы определить дробь, создайте объект класса Fraction и передайте ему числитель и знаменатель.
  3. Строка 5. С дробями можно выполнять все обычные математические операции. Все они возвращают новый объект класса Fraction. \(2 \times \frac{1}{3} = \frac{2}{3}\).
  4. Строка 7. Объект Fraction автоматически сократит дроби. \(\frac{6}{4} = \frac{3}{2}\).
  5. Строка 9. У Python хватает здравого смысла, чтобы не создавать дроби с нулевым знаменателем.

2.3.4 Тригонометрия

Ещё в Python можно работать с основными тригонометрическими функциями.

>>> import math
>>> math.pi                ①
3.1415926535897931
>>> math.sin(math.pi / 2)  ②
1.0
>>> math.tan(math.pi / 4)  ③
0.99999999999999989
  1. Строка 2. Модуль math содержит константу π – отношение длины окружности к её диаметру.
  2. Строка 4. Модуль math содержит все основные тригонометрические функции, включая sin(), cos(), tan(), и их варианты наподобие asin().
  3. Строка 6. Заметьте, однако, что точность расчетов в Python не бесконечна. Выражение \(\tan\left(\frac{\pi}{4}\right)\) должно возвращать значение 1.0, а не 0.99999999999999989.

2.3.5 Числа в логическом контексте

Нулевые значения – ложь, ненулевые значения – истина.

Вы можете использовать числа в логическом контексте, например, в операторе if. Нулевые значения – ложь, ненулевые значения – истина.

>>> def is_it_true(anything):             ①
...   if anything:
...     print("да, это истина")
...   else:
...     print("нет, это ложь")
...
>>> is_it_true(1)                         ②
да, это истина
>>> is_it_true(-1)
да, это истина
>>> is_it_true(0)
нет, это ложь
>>> is_it_true(0.1)                       ③
да, это истина
>>> is_it_true(0.0)
нет, это ложь
>>> import fractions
>>> is_it_true(fractions.Fraction(1, 2))  ④
да, это истина
>>> is_it_true(fractions.Fraction(0, 1))
нет, это ложь
  1. Строка 1. Вы знали, что можно определять свои собственные функции в интерактивной оболочке Python? Просто нажимайте клавишу Enter в конце каждой строки, а чтобы закончить ввод нажмите клавишу Enter на пустой строке.
  2. Строка 7. В логическом контексте, ненулевые целые числа – истина; значение 0 – ложь.
  3. Строка 13. Ненулевые числа с плавающей точкой – истина; значение 0.0 – ложь. Будьте осторожны с этим! Если имеется малейшая ошибка округления (как вы могли видеть в предыдущем разделе,это вполне возможно), то Python будет проверять значение 0.0000000000001 вместо 0.0 и соответственно вернёт логическое значение True.
  4. Строка 18. Дроби тоже могут быть использованы в логическом контексте. Fraction(0, n) – ложь для всех значений n. Все остальные дроби – истина.

2.4 Списки

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

Списки в Python похожи на массивы в Perl 5. Там переменные, содержащие массивы, всегда начинаются с символа @; в Python переменные могут называться как угодно, язык следит за типом самостоятельно.

В Python список – это нечто большее, чем массив в Java (хотя список можно использовать и как массив, если это действительно то, чего вы хотите от жизни). Точнее будет аналогия с Java-классом ArrayList, который может хранить произвольные объекты и динамически расширяться по мере добавления новых элементов.

2.4.1 Создание списка

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

>>> a_list = ['a', 'b', 'mpilgrim', 'z', 'example']  ①
>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[0]                                        ②
'a'
>>> a_list[4]                                        ③
'example'
>>> a_list[-1]                                       ④
'example'
>>> a_list[-3]                                       ⑤
'mpilgrim'
  1. Строка 1. Сначала вы определили список из пяти элементов. Обратите внимание, они сохраняют свой первоначальный порядок. Это не случайно. Список – это упорядоченный набор элементов.
  2. Строка 4. Список можно использовать как массив с нумерацией от нуля. Первый элемент не пустого списка будет всегда a_list[0].
  3. Строка 6. Последним элементом этого пятиэлементного списка будет a_list[4], потому что нумерация элементов в списке всегда начинается с нуля.
  4. Строка 8. Используя отрицательный индекс, можно обратиться к элементам по их номеру от конца списка. Последний элемент не пустого списка будет всегда a_list[-1].
  5. Строка 10. Если вас сбивают с толку отрицательные индексы, то просто думайте о них следующим образом: a_list[-n] == a_list[len(a_list) - n]. В нашем примере a_list[-3] == a_list[5 - 3] == a_list[2].

2.4.2 Разрезание списка

a_list[0] – первый элемент списка a_list.

После того, как список создан, можно получить любую его часть в виде списка. Это называется «slicing» – срез списка.

>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[1:3]            ①
['b', 'mpilgrim']
>>> a_list[1:-1]           ②
['b', 'mpilgrim', 'z']
>>> a_list[0:3]            ③
['a', 'b', 'mpilgrim']
>>> a_list[:3]             ④
['a', 'b', 'mpilgrim']
>>> a_list[3:]             ⑤
['z', 'example']
>>> a_list[:]              ⑥
['a', 'b', 'mpilgrim', 'z', 'example']
  1. Строка 3. Вы можете получить часть списка, называемую «срезом», указав два индекса. В результате получается новый список, включающий в себя элементы исходного в том же порядке, начиная с первого индекса среза (в данном случае a_list[1]), до последнего, но не включая его (в данном случае a_list[3]).
  2. Строка 5. Срез работает, даже если один или оба индекса отрицательны. Если вам это поможет, можете думать об этом так: список читается слева направо, первый индекс среза определяет первый нужный вам элемент, а второй индекс определяет первый элемент, который вам не нужен. Возвращаемое значение всегда находится между ними.
  3. Строка 7. Нумерация списков начинается с нуля, поэтому a_list[0:3] возвращает первые три элемента списка, начиная с a_list[0], заканчивая на (но не включая) a_list[3].
  4. Строка 9. Если левый индекс среза – 0, вы можете опустить его, 0 будет подразумеваться. Так, a_list[:3] – это то же самое,что и a_list[0:3], потому что начальный 0 подразумевается.
  5. Строка 11. Аналогично, если правый индекс среза является длиной списка, вы можете его опустить. Так, a_list[3:] – это то же самое, что и a_list[3:5], потому что этот список содержит пять элементов. Здесь прослеживается явная симметрия. В этом пятиэлементном списке a_list[:3] возвращает первые 3 элемента, а a_list[3:] возвращает последние два элемента. На самом деле, a_list[:n] всегда будет возвращать первые n элементов, а a_list[n:] будет возвращать все остальные, независимо от длины списка.
  6. Строка 13. Если оба индекса списка опущены, включаются все элементы списка. Но это не то же самое, что первоначальная переменная a_list. Это новый список, включающий все элементы исходного. Запись a_list[:] представляет собой простейший способ получения полной копии списка.

2.4.3 Добавление элементов в список

Существует четыре способа добавить элементы в список.

>>> a_list = ['a']
>>> a_list = a_list + [2.0, 3]    ①
>>> a_list                        ②
['a', 2.0, 3]
>>> a_list.append(True)           ③
>>> a_list
['a', 2.0, 3, True]
>>> a_list.extend(['four', 'Ω'])  ④
>>> a_list
['a', 2.0, 3, True, 'four', 'Ω']
>>> a_list.insert(0, 'Ω')         ⑤
>>> a_list
['Ω', 'a', 2.0, 3, True, 'four', 'Ω']
  1. Строка 2. Оператор + соединяет списки, создавая новый список. Список может содержать любое число элементов; ограничений размера не существует (пока есть доступная память). Однако, если вы заботитесь о памяти, знайте, что сложение списков создает ещё один список в памяти. В данном случае, этот новый список немедленно присваивается существующей переменной a_list. Так что эта строка кода, на самом деле, реализует двухэтапный процесс (сложение, а затем присвоение), который может (временно) потребовать много памяти, если вы имеете дело с большими списками.
  2. Строка 3. Список может содержать элементы любых типов, и элементы одного списка не обязательно должны быть одного и того же типа. Здесь мы видим список, содержащий строку, число с плавающей точкой и целое число.
  3. Строка 5. Метод append() добавляет один элемент в конец списка. (Теперь у нас в списке присутствуют четыре различных типа данных!)
  4. Строка 8. Списки реализованы как классы. «Создание» списка – это фактически создание экземпляра класса. Таким образом, у списка есть методы, которые работают с ним. Метод extend() принимает один аргумент – список, и добавляет каждый его элемент к исходному списку.
  5. Строка 11. Метод insert() вставляет элемент в список. Первым аргументом является индекс первого элемента в списке, который будет сдвинут новым элементом со своей позиции. Элементы списка не обязаны быть уникальными; например, теперь у нас есть два различных элемента со значением 'Ω': первый элемент a_list[0] и последний элемент a_list[6].

В Python конструкция a_list.insert(0, value) действует как функция unshift() в Perl. Она добавляет элемент в начало списка, а все другие элементы увеличивают свой индекс на единицу, чтобы освободить пространство.

Давайте подробнее рассмотрим разницу между append() и extend().

>>> a_list = ['a', 'b', 'c']
>>> a_list.extend(['d', 'e', 'f'])  ①
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f']
>>> len(a_list)                     ②
6
>>> a_list[-1]
'f'
>>> a_list.append(['g', 'h', 'i'])  ③
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f', ['g', 'h', 'i']]
>>> len(a_list)                     ④
7
>>> a_list[-1]
['g', 'h', 'i']
  1. Строка 2. Метод extend() принимает один аргумент, который всегда является списком, и добавляет каждый элемент этого списка к a_list.
  2. Строка 5. Если вы возьмёте список из трёх элементов и расширите его списком из ещё трёх элементов, в итоге получится список из шести элементов.
  3. Строка 9. С другой стороны, метод append() получает единственный аргумент, который может быть любого типа. Здесь мы вызываем метод append(), передавая ему список из трёх элементов.
  4. Строка 12. Если вы возьмёте список из шести элементов и добавите к нему список, в итоге вы получите... список из семи элементов. Почему семь? Потому что последний элемент (который мы только что добавили) является списком. Списки могут содержать любые типы данных, включая другие списки. Возможно, это то, что вам нужно, возможно, нет. Но это то, что вы просили, и это то, что вы получили.

2.4.4 Поиск значений в списке

>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list.count('new')       ①
2
>>> 'new' in a_list           ②
True
>>> 'c' in a_list
False
>>> a_list.index('mpilgrim')  ③
3
>>> a_list.index('new')       ④
2
>>> a_list.index('c')         ⑤
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: list.index(x): x not in list

Перевод сообщения оболочки

Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: list.index(x): x not in list
Раскрутка стека (от внешних к внутренним):
  Файл "<интерактивный ввод>", строка 1, позиция ?
ValueError: list.index(x): x не в списке
  1. Строка 2. Как вы наверное ожидаете, метод count возвращает количество вхождений указанного значения в список.
  2. Строка 4. Если всё, что вам нужно – это узнать, присутствует ли значение в списке или нет, тогда оператор in намного быстрее, чем метод count(). Оператор in всегда возвращает True или False; он не сообщает, сколько именно в списке данных значений.
  3. Строка 8. Если вам необходимо точно знать, на каком месте в списке находится какое-либо значение, то используйте метод index(). По умолчанию, он просматривает весь список, но вы можете указать вторым аргументом индекс (отсчитываемый от нуля), с которого необходимо начать поиск, и даже третий аргумент – индекс, на котором необходимо остановить поиск.
  4. Строка 10. Метод index() находит только первое вхождение значения в списке. В данном случае 'new' встречается дважды в списке: в a_list[2] и a_list[4], но метод index() вернёт только индекс первого вхождения.
  5. Строка 12. Вопреки вашим ожиданиям, если значение не найдено в списке, то метод index() возбудит исключение.

Постойте, что? Да, верно: метод index() возбуждает исключение, если не может найти значение в списке. Вы наверное заметили отличие от большинства других языков, которые возвращают какой-нибудь неверный индекс (например, -1). Если на первый взгляд это может немного раздражать, то, я думаю, в будущем вы примете этот подход. Это означает, что ваша программа будет аварийно завершена в том месте, где возникла проблема, вместо того чтобы тихо перестать работать в каком-нибудь другом месте. Запомните, -1 тоже является подходящим индексом для списков. Если бы метод index() возвращал -1, вас могли ожидать невесёлые вечера, потраченные на поиск ошибок в коде!

2.4.5 Удаление элементов из списка

Списки никогда не содержат разрывов.

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

>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list[1]
'b'
>>> del a_list[1]         ①
>>> a_list
['a', 'new', 'mpilgrim', 'new']
>>> a_list[1]             ②
'new'
  1. Строка 4. Можно использовать выражение del для удаления определенного элемента из списка.
  2. Строка 7. Если после удаления элемента с индексом 1 опять попытаться прочитать значение списка с индексом 1, это не вызовет ошибки. Все элементы после удаления смещают свои индексы, чтобы «заполнить пробел», возникший после удаления элемента.

Не знаете индекс? Не беда – можно удалить элемент по значению.

>>> a_list.remove('new')  ①
>>> a_list
['a', 'mpilgrim', 'new']
>>> a_list.remove('new')  ②
>>> a_list
['a', 'mpilgrim']
>>> a_list.remove('new')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list

Перевод сообщений оболочки

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.remove(x): x not in list
Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, <модуль>
ValueError: list.remove(x): x не в списке
  1. Строка 1. Можно удалить элемент из списка при помощи метода remove(). Метод remove() в качестве параметра принимает значение и удаляет первое вхождение этого значения из списка. Кроме того, индексы всех элементов, следующих за удалённым, будут сдвинуты, чтобы «заполнить пробел». Списки никогда не содержат разрывов.
  2. Строка 4. Можно вызывать метод remove() столько, сколько хотите, однако если попытаться удалить значение, которого нет в списке, будет порождено исключение.

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

Другой интересный метод списков – pop(). Метод pop() – это еще один способ удалить элементы из списка, но с одной особенностью.

>>> a_list = ['a', 'b', 'new', 'mpilgrim']
>>> a_list.pop()   ①
'mpilgrim'
>>> a_list
['a', 'b', 'new']
>>> a_list.pop(1)  ②
'b'
>>> a_list
['a', 'new']
>>> a_list.pop()
'new'
>>> a_list.pop()
'a'
>>> a_list.pop()   ③
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: pop from empty list

Перевод сообщения оболочки

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: pop from empty list
Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, <модуль>
IndexError: pop из пустого списка
  1. Строка 2. Если вызвать pop() без аргументов, он удалит последний элемент списка и вернет удаленное значение.
  2. Строка 6. С помощью метода pop() можно удалить любой элемент списка. Просто вызовите метод с индексом элемента. Этот элемент будет удалён, а все элементы после него сместятся, чтобы «заполнить пробел». Метод возвращает удалённое из списка значение.
  3. Строка 14. Метод pop() для пустого списка возбуждает исключение.

Вызов метода pop() без аргументов эквивалентен вызову функции pop() в Perl. Он удаляет последний элемент из списка и возвращает удалённое значение. В языке программирования Perl есть также функция shift(), которая удаляет первый элемент и возвращает его значение. В Python это эквивалентно a_list.pop(0).

2.4.7 Списки в логическом контексте

Пустые списки – ложь, все остальные – истина.

Вы также можете использовать список в логическом контексте, например, в операторе if:

>>> def is_it_true(anything):
...   if anything:
...     print("да, это истина")
...   else:
...     print("нет, это ложь")
...
>>> is_it_true([])             ①
нет, это ложь
>>> is_it_true(['a'])          ②
да, это истина
>>> is_it_true([False])        ③
да, это истина
  1. Строка 7. В логическом контексте пустой список – ложь.
  2. Строка 9. Любой список, состоящий хотя бы из одного элемента, – истина.
  3. Строка 11. Любой список, состоящий хотя бы из одного элемента, – истина. Значения элементов не важны.

2.5 Кортежи

Кортеж – это неизменяемый список. После создания кортеж не может быть изменён никаким способом.

>>> a_tuple = ("a", "b", "mpilgrim", "z", "example")  ①
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple[0]                                        ②
'a'
>>> a_tuple[-1]                                       ③
'example'
>>> a_tuple[1:3]                                      ④
('b', 'mpilgrim')
  1. Строка 1. Кортеж определяется так же, как список, за исключением того, что набор элементов заключается в круглые скобки, а не в квадратные.
  2. Строка 4. Элементы кортежа заданы в определённом порядке, как и в списке. Элементы кортежа индексируются с нуля, как и элементы списка, таким образом первый элемент не пустого кортежа – это всегда a_tuple[0].
  3. Строка 6. Отрицательные значения индекса отсчитываются от конца кортежа, как и в списке.
  4. Строка 8. Создание среза кортежа («slicing») аналогично созданию среза списка. Когда создаётся срез списка, получается новый список; когда создаётся срез кортежа, получается новый кортеж.

Основное отличие между кортежами и списками состоит в том, что кортежи не могут быть изменены. Говоря техническим языком, кортеж – неизменяемый объект. На практике это означает, что у них нет методов, которые бы позволили их изменить. У списков есть такие методы, как append(), extend(), insert(), remove(), и pop(). У кортежей ни одного из этих методов нет. Можно взять срез от кортежа (так как при этом создастся новый кортеж), можно проверить, содержит ли кортеж элемент с конкретным значением (так как это действие не изменит кортеж), и... собственно, всё.

# продолжение предыдущего примера
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple.append("new")               ①
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'append'
>>> a_tuple.remove("z")                 ②
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'remove'
>>> a_tuple.index("example")            ③
4
>>> "z" in a_tuple                      ④
True

Перевод сообщений оболочки

Раскрутка стека (от внешних к внутренним):
  Файл "<интерактивный ввод>", строка 1, позиция ?
AttributeError: у объекта 'tuple' нет атрибута '<атрибут>'
  1. Строка 4. Вы не можете добавить элементы к кортежу. Кортежи не имеют методов append() или extend().
  2. Строка 8. Вы не можете удалять элементы из кортежа. Кортежи не имеют методов remove() или pop().
  3. Строка 12. Вы можете искать элементы в кортежи, поскольку это не изменяет кортеж.
  4. Строка 14. Вы также можете использовать оператор in, чтобы проверить существует ли элемент в кортеже.

Так где же могут пригодиться кортежи?

  • Кортежи быстрее, чем списки. Если вы определяете неизменяемый набор значений и всё, что вы собираетесь с ним делать – итерировать по нему, используйте кортеж вместо списка.
  • Кортежи делают код безопаснее в том случае, если у вас есть «защищенные от записи» данные, которые не должны изменяться. Использование кортежей вместо списков избавит вас от необходимости использовать оператор assert, дающий понять, что данные неизменяемы, и что нужно приложить особые усилия (и особую функцию), чтобы это обойти.
  • Некоторые кортежи могут использоваться в качестве ключей словаря (конкретно, кортежи, содержащие неизменяемые значения, например, строки, числа и другие кортежи). Списки никогда не могут использоваться в качестве ключей словаря, потому что списки – изменяемые объекты.

Кортежи могут быть преобразованы в списки и наоборот. Встроенная функция tuple() принимает список и возвращает кортеж из всех его элементов, функция list() принимает кортеж и возвращает список. По сути дела, tuple() замораживает список, а list() размораживает кортеж.

2.5.1 Кортежи в логическом контексте

Вы можете использовать кортежи в логическом контексте, например, в операторе if.

>>> def is_it_true(anything):
...   if anything:
...     print("да, это истина")
...   else:
...     print("нет, это ложь")
...
>>> is_it_true(())             ①
нет, это ложь
>>> is_it_true(('a', 'b'))     ②
да, это истина
>>> is_it_true((False,))       ③
да, это истина
>>> type((False))              ④
<class 'bool'>
>>> type((False,))
<class 'tuple'>
  1. Строка 7. В логическом контексте пустой кортеж является ложью.
  2. Строка 9. Любой кортеж состоящий по крайней мере из одного элемента – истина.
  3. Строка 11. Любой кортеж состоящий по крайней мере из одного элемента – истина. Значения элементов не важны. Но что делает здесь эта запятая?
  4. Строка 13. Чтобы создать кортеж из одного элемента, необходимо после него поставить запятую. Без запятой Python предполагает, что вы просто добавили еще одну пару скобок, что не делает ничего плохого, но и не создает кортеж.

2.5.2 Присваивание нескольких значений за раз

Вот крутой программерский прием: в Python можно использовать кортежи, чтобы присваивать значение нескольким переменным сразу.

>>> v = ('a', 2, True)
>>> (x, y, z) = v       ①
>>> x
'a'
>>> y
2
>>> z
True
  1. Строка 2.v – это кортеж из трех элементов, а (x, y, z) – кортеж из трёх переменных. Присвоение одного другому приводит к присвоению каждого значения из v каждой переменной в указанном порядке.

Это не единственный способ использования кортежей. Предположим, что вы хотите присвоить имена диапазону значений. Вы можете использовать встроенную функцию range() для быстрого присвоения сразу нескольких последовательных значений.

>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)  ①
>>> MONDAY                                                                       ②
0
>>> TUESDAY
1
>>> SUNDAY
6
  1. Строка 1. Встроенная функция range() создаёт последовательность целых чисел. (Строго говоря, функция range() возвращает итератор, а не список или кортеж, но вы узнаете разницу чуть позже.) MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, и SUNDAY – определяемые переменные. (Этот пример заимствован из модуля calendar, небольшого забавного модуля, который отображает календари, примерно как программа cal из UNIX. В этом модуле определяются константы целого типа для дней недели.)
  2. Строка 2. Теперь каждой переменной присвоено конкретное значение: MONDAY – это 0, TUESDAY – 1, и так далее.

Вы также можете использовать присвоение значений нескольким переменным сразу, чтобы создавать функции, возвращающие несколько значений, для этого достаточно просто вернуть кортеж, содержащий эти значения. В том месте программы, где была вызвана функция, возвращаемое значение можно использовать как кортеж целиком, или присвоить значения нескольким отдельным переменным. Этот приём используется во многих стандартных библиотеках Python, включая и модуль os, о котором вы узнаете в следующей главе.

2.6 Множества

Множество – это «мешок», содержащий неупорядоченные уникальные значения. Одно множество может содержать значения любых типов. Если у вас есть два множества, вы можете совершать над ними любые стандартные операции, например, объединение, пересечение и разность.

2.6.1 Создание множества

Начнём с самого начала. Создать множество очень легко.

>>> a_set = {1}     ①
>>> a_set
{1}
>>> type(a_set)     ②
<class 'set'>
>>> a_set = {1, 2}  ③
>>> a_set
{1, 2}
  1. Строка 1. Чтобы создать множество с одним значением, поместите его в фигурные скобки ({}).
  2. Строка 4. Множества, вообще-то, реализуются как классы, но пока не беспокойтесь об этом.
  3. Строка 6. Чтобы создать множество с несколькими значениями, отделите их друг от друга запятыми и поместите внутрь фигурных скобок.

Также вы можете создать множество из списка.

>>> a_list = ['a', 'b', 'mpilgrim', True, False, 42]
>>> a_set = set(a_list)                           ①
>>> a_set                                         ②
{'a', False, 'b', True, 'mpilgrim', 42}
>>> a_list                                        ③
['a', 'b', 'mpilgrim', True, False, 42]
  1. Строка 2. Чтобы создать множество из списка, воспользуйтесь функцией set(). (Педанты, которые знают как реализованы множества, отметят, что на самом деле это создание экземпляра класса, а не вызов функции. Я обещаю, вы узнаете в чём разница далее в этой книге. Сейчас просто знайте, что set() ведет себя как функция и возвращает множество.)
  2. Строка 3. Как я упоминал ранее, множество может содержать значения любых типов. И, как я упоминал ранее, множества неупорядочены. Это множество не помнит первоначальный порядок списка, из которого оно было создано. Если вы добавляете элементы в множество, оно не запоминает, в каком порядке они добавлялись.
  3. Строка 5. Исходный список не изменился.

У вас ещё нет значений? Нет проблем. Можно создать пустое множество.

>>> a_set = set()    ①
>>> a_set            ②
set()
>>> type(a_set)      ③
<class 'set'>
>>> len(a_set)       ④
0
>>> not_sure = {}    ⑤
>>> type(not_sure)
<class 'dict'>
  1. Строка 1. Чтобы создать пустое множество, вызовите set() без аргументов.
  2. Строка 2. Напечатанное представление пустого множества выглядит немного странно. Вы, наверное, ожидали увидеть {}? Это означало бы пустой словарь, а не пустое множество. О словарях вы узнаете далее в этой главе.
  3. Строка 4. Несмотря на странное печатное представление, это действительно множество...
  4. Строка 6. ...и это множество не содержит ни одного элемента.
  5. Строка 8. В силу исторических причуд, пришедших из Python 2, нельзя создать пустое множество с помощью двух фигурных скобок. На самом деле, они создают пустой словарь, а не множество.

2.6.2 Изменение множества

Есть два способа добавить элементы в существующее множество: метод add() и метод update().

>>> a_set = {1, 2}
>>> a_set.add(4)  ①
>>> a_set
{1, 2, 4}
>>> len(a_set)    ②
3
>>> a_set.add(1)  ③
>>> a_set
{1, 2, 4}
>>> len(a_set)    ④
3
  1. Строка 2. Метод add() принимает один аргумент, который может быть любого типа, и добавляет данное значение в множество.
  2. Строка 5. Теперь множество содержит 3 элемента.
  3. Строка 7. Множества – мешки уникальных значений. Если попытаться добавить значение, которое уже присутствует в множестве, ничего не произойдет. Это не приведет в возникновению ошибки; просто нулевое действие.
  4. Строка 10. Это множество все ещё состоит из 3 элементов.
>>> a_set = {1, 2, 3}
>>> a_set
{1, 2, 3}
>>> a_set.update({2, 4, 6})                       ①
>>> a_set                                         ②
{1, 2, 3, 4, 6}
>>> a_set.update({3, 6, 9}, {1, 2, 3, 5, 8, 13})  ③
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 13}
>>> a_set.update([10, 20, 30])                    ④
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 10, 13, 20, 30}
  1. Строка 4. Метод update() принимает один аргумент – множество, и добавляет все его элементы к исходному множеству. Так, как если бы вы вызывали метод add() и по очереди передавали ему все элементы множества.
  2. Строка 5. Повторяющиеся значения игнорируются, поскольку множество не может содержать дубликаты.
  3. Строка 7. Вообще-то, вы можете вызвать метод update() с любым количеством параметров. Когда он вызывается с двумя множествами, метод update() добавляет все элементы обоих множеств в исходное множество (пропуская повторяющиеся).
  4. Строка 10. Метод update() может принимать объекты различных типов, включая списки. Когда ему передается список, он добавляет все его элементы в исходное множество.

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

Существуют три способа удаления отдельных значений из множества. Первые два, discard() и remove(), немного различаются.

>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set
{1, 3, 36, 6, 10, 45, 15, 21, 28}
>>> a_set.discard(10)                        ①
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.discard(10)                        ②
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.remove(21)                         ③
>>> a_set
{1, 3, 36, 6, 45, 15, 28}
>>> a_set.remove(21)                         ④
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 21

Перевод сообщения оболочки

Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, <модуль>
KeyError: 21
  1. Строка 4. Метод discard() принимает в качестве аргумента одиночное значение и удаляет это значение из множества.
  2. Строка 7. Если вы вызвали метод discard() передав ему значение, которого нет в множестве, ничего не произойдет, просто нулевое действие.
  3. Строка 10. Метод remove() также принимает в качестве аргумента одиночное значение, и также удаляет его из множества.
  4. Строка 13. Вот в чём отличие: если значения нет в множестве, метод remove() породит исключение KeyError.

Подобно спискам, у множеств есть метод pop().

>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set.pop()                                ①
1
>>> a_set.pop()
3
>>> a_set.pop()
36
>>> a_set
{6, 10, 45, 15, 21, 28}
>>> a_set.clear()                              ②
>>> a_set
set()
>>> a_set.pop()                                ③
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'pop from an empty set'

Перевод сообщения оболочки

Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, <модуль>
KeyError: 'pop из пустого множества'
  1. Строка 2. Метод pop() удаляет один элемент из множества и возвращает его значение. Однако, поскольку множества неупорядочены, это не «последний» элемент в множестве, поэтому невозможно проконтролировать какое значение было удалено. Удаляется произвольный элемент.
  2. Строка 10. Метод clear() удаляет все элементы множества, оставляя вас с пустым множеством. Это эквивалентно записи a_set = set(), которая создаст новое пустое множество и перезапишет предыдущее значение переменной a_set.
  3. Строка 13. Попытка извлечения (pop()) элемента из пустого множества породит исключение KeyError.

2.6.4 Основные операции с множествами

Тип set в Python поддерживает несколько основных операций над множествами.

>>> a_set = {2, 4, 5, 9, 12, 21, 30, 51, 76, 127, 195}
>>> 30 in a_set                                                     ①
True
>>> 31 in a_set
False
>>> b_set = {1, 2, 3, 5, 6, 8, 9, 12, 15, 17, 18, 21}
>>> a_set.union(b_set)                                              ②
{1, 2, 195, 4, 5, 6, 8, 12, 76, 15, 17, 18, 3, 21, 30, 51, 9, 127}
>>> a_set.intersection(b_set)                                       ③
{9, 2, 12, 5, 21}
>>> a_set.difference(b_set)                                         ④
{195, 4, 76, 51, 30, 127}
>>> a_set.symmetric_difference(b_set)                               ⑤
{1, 3, 4, 6, 8, 76, 15, 17, 18, 195, 127, 30, 51}
  1. Строка 2. Чтобы проверить, принадлежит ли значение множеству, используйте оператор in. Он работает так же, как и для списков.
  2. Строка 7. Метод union() (объединение) возвращает новое множество, содержащее все элементы каждого из множеств.
  3. Строка 9. Метод intersection() (пересечение) возвращает новое множество, содержащее все элементы, которые есть и в первом множестве, и во втором.
  4. Строка 11. Метод difference() (разность) возвращает новое множество, содержащее все элементы, которые есть в множестве a_set, но которых нет в множестве b_set.
  5. Строка 13. Метод symmetric_difference() (симметрическая разность) возвращает новое множество, которое содержит только уникальные элементы обоих множеств.

Три из этих методов симметричны.

# продолжение предыдущего примера
>>> b_set.symmetric_difference(a_set)                                       ①
{3, 1, 195, 4, 6, 8, 76, 15, 17, 18, 51, 30, 127}
>>> b_set.symmetric_difference(a_set) == a_set.symmetric_difference(b_set)  ②
True
>>> b_set.union(a_set) == a_set.union(b_set)                                ③
True
>>> b_set.intersection(a_set) == a_set.intersection(b_set)                  ④
True
>>> b_set.difference(a_set) == a_set.difference(b_set)                      ⑤
False
  1. Строка 2. Симметрическая разность множеств a_set и b_set выглядит не так, как симметрическая разность множеств b_set и a_set, но вспомните, множества неупорядочены. Любые два множества, все (без исключения) значения которых одинаковы, считаются равными.
  2. Строка 4. Именно это здесь и произошло. Глядя на печатное представление этих множеств, созданное оболочкой Python, не обманывайтесь. Значения элементов этих множеств одинаковы, поэтому они равны.
  3. Строка 6. Объединение двух множеств также симметрично.
  4. Строка 8. Пересечение двух множеств также симметрично.
  5. Строка 10. Разность двух множеств несимметрична. По смыслу, данная операция аналогична вычитанию одного числа из другого. Порядок операндов имеет значение.

Наконец, есть ещё несколько вопросов по множествам, которые вы можете задать.

>>> a_set = {1, 2, 3}
>>> b_set = {1, 2, 3, 4}
>>> a_set.issubset(b_set)    ①
True
>>> b_set.issuperset(a_set)  ②
True
>>> a_set.add(5)             ③
>>> a_set.issubset(b_set)
False
>>> b_set.issuperset(a_set)
False
  1. Строка 3. Множество a_set является подмножеством b_set – все элементы a_set также являются элементами b_set.
  2. Строка 5. И наоборот, b_set является надмножеством a_set, потому что все элементы a_set также являются элементами b_set.
  3. Строка 7. Поскольку вы добавили элемент в a_set, но не добавили в b_set, обе проверки вернут значение False.

2.6.5 Множества в логическом контексте

Вы можете использовать множества в логическом контексте, например, в операторе if.

>>> def is_it_true(anything):
...   if anything:
...     print("да, это истина")
...   else:
...     print("нет, это ложь")
...
>>> is_it_true(set())          ①
нет, это ложь
>>> is_it_true({'a'})          ②
да, это истина
>>> is_it_true({False})        ③
да, это истина
  1. Строка 7. В логическом контексте пустое множество – ложь.
  2. Строка 9. Любое множество, содержащее хотя бы один элемент – истина.
  3. Строка 11. Любое множество, содержащее хотя бы один элемент – истина. Значения элементов не важны.

2.7 Словари

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

Словарь в Python аналогичен хэшу в Perl 5. В Perl 5 переменные, хранящие хэши, всегда начинаются с символа %. В Python переменные могут быть названы как угодно, язык сам отслеживает типы данных.

2.7.1 Создание словаря

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

>>> a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'}  ①
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['server']                                                    ②
'db.diveintopython3.org'
>>> a_dict['database']                                                  ③
'mysql'
>>> a_dict['db.diveintopython3.org']                                    ④
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'db.diveintopython3.org'

Перевод сообщения оболочки

Раскрутка стека (список последних вызовов):
  Файл "<stdin>", строка 1, <модуль>
KeyError: 'db.diveintopython3.org'
  1. Строка 1. Сначала вы создаёте новый словарь с двумя элементами и присваиваете его переменной a_dict. Каждый элемент является парой ключ–значение, а весь набор элементов заключён в фигурные скобки.
  2. Строка 4. 'server' является ключом, и он связан со значением, обращение к которому с помощью a_dict['server'] даст нам 'db.diveintopython3.org'.
  3. Строка 6. 'database' является ключом, и он связан со значением, обращение к которому с помощью a_dict['database'] даст нам 'mysql'.
  4. Строка 8. Можно получить значение по ключу, но нельзя получить ключи по значению. Так a_dict['server'] – это 'db.diveintopython3.org', но a_dict['db.diveintopython3.org'] породит исключение, потому что 'db.diveintopython3.org' не является ключом.

2.7.2 Изменение словаря

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

>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['database'] = 'blog'  ①
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'blog'}
>>> a_dict['user'] = 'mark'      ②
>>> a_dict                       ③
{'server': 'db.diveintopython3.org', 'user': 'mark', 'database': 'blog'}
>>> a_dict['user'] = 'dora'      ④
>>> a_dict
{'server': 'db.diveintopython3.org', 'user': 'dora', 'database': 'blog'}
>>> a_dict['User'] = 'mark'      ⑤
>>> a_dict
{'User': 'mark', 'server': 'db.diveintopython3.org', 'user': 'dora', 'database': 'blog'}
  1. Строка 3. Ваш словарь не может содержать одинаковые ключи. Присвоение значения существующему ключу уничтожит старое значение.
  2. Строка 6. Можно добавлять новые пары ключ-значение когда угодно. Данный синтаксис идентичен синтаксису модифицирования существующих значений.
  3. Строка 7. Кажется, что новый элемент словаря (ключ 'user', значение 'mark') попал в середину. На самом деле, это всего лишь совпадение, что элементы кажутся расположенными по порядку в первом примере; такое же совпадение, что теперь они выглядят расположенными не по порядку.
  4. Строка 9. Присвоение значения существующему ключу просто заменяет старое значение новым.
  5. Строка 12. Изменится ли значение ключа 'user' обратно на "mark"? Нет! Посмотрите на него внимательнее – ключ "User" написан с заглавной буквы. Ключи словаря регистрозависимы, поэтому это выражение создаст новую пару ключ-значение, а не перезапишет существующую. Вам кажется, что ключи похожи, а с точки зрения Python они абсолютно разные.

2.7.3 Словари со смешанными значениями

Словари могут состоять не только из строк. Значения словарей могут быть любого типа, включая целые числа, логические значения, произвольные объекты, или даже другие словари. И значения в одном словаре не обязаны быть одного и того же типа; можно смешивать и сочетать их, как вам необходимо. Ключи словаря более ограниченны, но они могут быть строками, целыми числами и некоторыми другими типами. Ключи разных типов тоже можно смешивать и сочетать в одном словаре.

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

SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}

Давайте вытащим эту переменную из нашей программы и поработаем с ней в интерактивной оболочке Python.

>>> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
...             1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
>>> len(SUFFIXES)      ①
2
>>> 1000 in SUFFIXES   ②
True
>>> SUFFIXES[1000]     ③
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
>>> SUFFIXES[1024]     ④
['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
>>> SUFFIXES[1000][3]  ⑤
'TB'
  1. Строка 3. Так же, как для списков и множеств, функция len() возвращает количество элементов в словаре.
  2. Строка 5. И так же, как со списками и множествами, можно использовать оператор in, чтобы проверить, определён ли конкретный ключ в словаре.
  3. Строка 7.1000 является ключом в словаре SUFFIXES; его значение – список из восьми элементов (восьми строк, если быть точным).
  4. Строка 9. Аналогично, 1024 – ключ словаря SUFFIXES; и его значение также является списком из восьми элементов.
  5. Строка 11. Так как SUFFIXES[1000] является списком, то можно обратиться к отдельным элементам списка по их порядковым номерам, которые индексируются с нуля.

2.7.4 Словари в логическом контексте

Пустые словари – ложь, все остальные – истина.

Вы можете использовать словари в логическом контексте, например в операторе if.

>>> def is_it_true(anything):
...   if anything:
...     print("да, это истина")
...   else:
...     print("нет, это ложь")
...
>>> is_it_true({})             ①
нет, это ложь
>>> is_it_true({'a': 1})       ②
да, это истина
  1. Строка 7. В логическом контексте пустой словарь ложен.
  2. Строка 9. Любой словарь с хотя бы одной парой ключ-значение истинен.

2.8 Константа None

None – это специальная константа в Python. Она обозначает пустое значение. None – это не то же самое, что False. None также и не 0. None даже не пустая строка. Если сравнивать None с другими типами данных, то результатом всегда будет False.

None – это просто пустое значение. None имеет свой собственный тип (NoneType). Вы можете присвоить None любой переменной, но вы не можете создавать других объектов типа NoneType. Все переменные, значение которых None равны друг другу.

>>> type(None)
<class 'NoneType'>
>>> None == False
False
>>> None == 0
False
>>> None == ''
False
>>> None == None
True
>>> x = None
>>> x == None
True
>>> y = None
>>> x == y
True

2.8.1 None в логическом контексте

В логическом контекстеNone всегда является ложью, а not None – истиной.

>>> def is_it_true(anything):
...   if anything:
...     print("да, это истина")
...   else:
...     print("нет, это ложь")
...
>>> is_it_true(None)
нет, это ложь
>>> is_it_true(not None)
да, это истина

2.9 Материалы для дальнейшего чтения

Источник:

  • Mark Pilgrim. Dive Into Python 3

Теги

PythonОбучениеПрограммированиеЯзыки программирования

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.