Глава 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) – кортеж из трёх переменных. Присвоение одного другому приводит к присвоению

    Теги

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