Доброе имя лучше большого богатства.
Притчи 22:1
Все, что хранится в компьютере, представляет собой лишь последовательность битов (приложение А). Одно из преимуществ вычислительной техники заключается в том, что мы можем интерпретировать эти биты любым удобным нам способом — как данные всевозможного размера и типов (например, как числа или текстовые символы) или даже как компьютерный код. Мы используем Python для определения наборов этих битов, соответствующих разным задачам, а также для отправки их в процессор и получения обратно.
Мы начнем с типов данных, используемых в Python, и значений, которые они могут содержать. Затем рассмотрим, как представить данные в виде значений-литералов и переменных.
Память вашего компьютера визуально можно представить в виде длинного ряда полок. Каждый слот на этих полках имеет ширину 1 байт (8 бит). Слоты пронумерованы от 0 (первая позиция) до самого конца. Современные компьютеры имеют миллиарды байт памяти (гигабайт), поэтому полки могли бы заполнить огромный воображаемый склад.
Программа Python получает доступ к определенной области памяти вашего компьютера с помощью операционной системы. Эта память используется для кода самой программы, а также для данных, которыми программа оперирует. Операционная система гарантирует, что программа не может читать или записывать в другие области памяти без соответствующего разрешения.
Программы следят за тем, где (область памяти) хранятся их биты и чем (тип данных) они являются. С точки зрения вашего компьютера все биты одинаковы. Одни и те же биты могут иметь разные значения в зависимости от того, какого они типа. Один и тот же вариант расстановки битов может означать как число 65, так и текстовый символ А.
Разные типы используют разное количество битов. Когда вы читаете о «64-битной машине», это означает, что целое число использует 64 бита (8 байт).
Некоторые языки хранят эти необработанные значения в памяти, отслеживая их размеры и типы. Вместо непосредственной обработки таких данных Python упаковывает каждое значение — булевы значения, целые числа, числа с плавающей точкой, строки и даже крупные структуры данных, функции и программы — в память как объекты. Глава 10 этой книги посвящена созданию собственных объектов в Python, но сейчас мы поговорим только об объектах, которые обрабатывают встроенные типы данных.
Продолжая аналогию с полками: можно представить, что объекты — это коробки переменной длины, занимающие место на этих полках так, как показано на рис. 2.1. Python создает такие коробки, размещает их на свободных местах и убирает, когда потребность в них отпадает.
Рис. 2.1. Похожий на коробку объект — целое число со значением 7
В Python объектом является фрагмент данных, в котором содержится как минимум следующее:
•тип, определяющий, что объект может делать (подробнее см. в следующем разделе);
• уникальный идентификатор, позволяющий отличить его от других объектов;
• значение, соответствующее типу;
• счетчик ссылок для отслеживания того, как часто объект используется.
Идентификатор представляет собой адрес места на полке. Тип похож на фабричный штамп на коробке, который поясняет, что объект может делать. Если объект является целым числом, он имеет тип int и может (помимо всего прочего, о чем вы узнаете из главы 3) быть добавлен к другому объекту с типом int. Если мы представим, что коробка сделана из прозрачного пластика, то сможем увидеть значение, находящееся внутри нее. О том, зачем нужен счетчик ссылок, вы узнаете через несколько разделов, когда мы будем говорить о переменных и именах.
В табл. 2.1 представлены базовые типы данных в Python. Во втором столбце («Тип») содержится имя этого типа в Python. Третий столбец («Изменяемый?») указывает, можно ли изменить значение переменной после ее создания (подробнее об этом поговорим в следующем разделе). В столбце «Примеры» показываются один или несколько примеров-литералов, соответствующих этому типу. И последний столбец («Глава») указывает на главу этой книги с наиболее подробной информацией о данном типе.
Таблица 2.1. Базовые типы данных в Python
Имя | Тип | Изменяемый? | Примеры | Глава |
Булево значение | bool | Нет | True, False | Глава 3 |
Целое число | int | Нет | 47, 25000, 25_000 | Глава 3 |
Число с плавающей точкой | float | Нет | 3.14, 2.7e5 | Глава 3 |
Комплексное число | complex | Нет | 3j, 5 + 9j | Глава 22 |
Текстовая строка | str | Нет | 'alas', "alack", '''a verse attack''' | Глава 5 |
Список | list | Да | ['Winken', 'Blinken', 'Nod'] | Глава 7 |
Кортеж | tuple | Нет | (2, 4, 8) | Глава 7 |
Байты | bytes | Нет | b'ab\xff' | Глава 12 |
Массив байтов | bytearray | Да | bytearray(…) | Глава 12 |
Множество | set | Да | set([3, 5, 7]) | Глава 8 |
Фиксированное множество | frozenset | Нет | frozenset(['Elsa', 'Otto']) | Глава 8 |
Словарь | dict | Да | {'game': 'bingo', 'dog': 'dingo', 'drummer': 'Ringo'} | Глава 8 |
После глав, посвященных этим базовым типам, в главе 10 вы узнаете, как создавать новые типы данных.
Изменчивость одна лишь неизменна.
Перси Шелли
Тип также определяет, можно ли значение, которое хранится в ящике, изменить — тогда это будет изменяемоезначение, или оно константно — неизменяемое значение. Неизменяемый объект как будто находится в закрытом ящике с прозрачными стенками (см. рис. 2.1): увидеть значение вы можете, но не в силах его изменить. По той же аналогии изменяемый объект похож на коробку с крышкой: вы можете не только увидеть хранящееся там значение, но и изменить его, не изменив его тип.
Python является строго типизированным языком, а это означает, что тип объекта не изменяется, даже если его значение изменяемо (рис. 2.2).
Рис. 2.2. Строгая типизация не означает, что клавиши нужно нажимать сильнее
Существует два вида определения данных в Python:
• как литералы;
• как переменные.
В следующих главах вы увидите, как указываются значения-литералы для разных типов данных — целые числа представляют собой последовательность цифр, дробные числа содержат десятичную точку, текстовые строки заключаются в кавычки и т.д. Но в примерах этой главы — чтобы избежать излишней сложности — мы будем использовать лишь короткие целые числа из десятичной системы счисления и один-два списка. Десятичные целые числа такие же, как числа в математике: они представляют собой последовательность цифр от 0 до 9. В главе 3 мы рассмотрим дополнительные детали работы с целыми числами (например, знаки и недесятичные системы счисления).
Вот мы и добрались до ключевого понятия языков программирования.
Python, как и большинство других компьютерных языков, позволяет вам определять переменные — имена для значений в памяти вашего компьютера, которые вы далее будете использовать в программе.
Имена переменных в Python отвечают определенным правилам.
• Они могут содержать только следующие символы:
• буквы в нижнем регистре (от a до z);
• буквы в верхнем регистре (от A до Z);
• цифры (от 0 до 9);
• нижнее подчеркивание (_).
• Они чувствительны к регистру: thing, Thing и THING — это разные имена.
• Они должны начинаться с буквы или нижнего подчеркивания, но не с цифры.
• Python особо обрабатывает имена, которые начинаются с нижнего подчеркивания (об этом вы сможете прочитать в главе 9).
• Они не могут совпадать с зарезервированными словами Python (их также называют ключевыми).
Перед вами список зарезервированных слов:
False await else import pass
None break except in raise
True class finally is return
and continue for lambda try
as def from nonlocal while
assert del global not with
async elif if or yield
Внутри программы Python увидеть список зарезервированных слов можно с помощью команд:
>>> help("keywords")
или:
>>> import keyword
>>> keyword.kwlist
Корректными являются такие имена:
•a;
• a1;
• a_b_c___95;
• _abc;
•_1a.
А следующие имена некорректны:
•1;
• 1a;
• 1_;
• name!;
•another-name.
В Python символ = применяется для присваивания значения переменной.
В школе нас учили, что символ = означает «равно». Почему же во многих языках программирования, включая Python, этот символ используется для обозначения присваивания? Одна из причин — на стандартной клавиатуре отсутствуют логические альтернативы вроде стрелки влево, а символ = не слишком сбивает с толку. Кроме того, в компьютерных программах присваивание используется чаще, чем проверка на равенство.
Программы непохожи на алгебру. В школе мы имели дело с подобными уравнениями:
y = x + 12
Решить уравнение можно, подставив значение для x. Если вы зададите для x значение 5, то, поскольку 5+12 равно 17, значение y будет равно 17. Подставьте значение 6, и y будет равен 18. И так далее.
Строки компьютерной программы могут выглядеть как уравнения, но означают они при этом нечто иное. В Python и других компьютерных языках x и y являются переменными. Python знает, что цифра или простая последовательность цифр вроде 12 или 5 является числовым литералом. Рассмотрим небольшую программу на Python, которая схожа с этим уравнением, — она выводит на экран значение y:
>>> x = 5
>>> y = x + 12
>>> y
17
Здесь мы видим большое различие между математикой и программами: в математике знак = означает равенство обеих сторон, а в программировании он означает присваивание: переменной слева мы присваиваем значение с правой стороны.
В программировании также принято, что все находящееся справа от знака = должно иметь значение (это называется инициализацией). Справа вы можете увидеть значение-литерал, переменную, которой было присвоено значение, или их комбинацию. Python знает, что 5 и 12 — это числовые литералы. В первой строке целочисленное значение 5 присваивается переменной х. Теперь мы можем использовать переменную х в следующей строке. Когда Python читает выражение y=x+12, он делает следующее:
• видит знак = в середине;
• понимает, что это оператор присваивания;
• вычисляет значение с правой стороны (получает значение объекта, на который ссылается переменная х, и добавляет его к 12);
• присваивает этот результат переменной слева — y.
Теперь, введя имя у в интерактивном интерпретаторе, можно увидеть его новое значение.
Если вы начнете программу со строки y=x+12, Python сгенерирует исключение (ошибку), поскольку переменная х еще не имеет значения:
>>> y = x + 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
Более подробно об исключениях можно прочитать в главе 9. На компьютерном языке мы скажем, что переменная хне была инициализирована.
В алгебре вы могли бы сделать все наоборот — присвоить значение у, чтобы подсчитать значение х. Для того, чтобы сделать это в Python, вам нужно получить значения-литералы и инициализированные переменные с правой стороны оператора присваивания до того, как присвоить значение переменной х:
>>> y = 5
>>> x = 12 - y
>>> x
7
Пришло время сделать важное утверждение о переменных в Python: переменные — всего лишь имена, и в этом заключается отличие Python от других языков программирования. Об этом важно помнить, особенно при работе с такими изменяемыми объектами, как списки. Операция присваивания не копирует значение, а только лишь прикрепляет имя к объекту, содержащему нужные данные. Имя — это ссылка на объект, а не сам объект. Можно представить, что имя — это этикетка, приклеенная на коробку с объектом, которая размещается где-то в памяти компьютера (рис. 2.3).
В других языках программирования переменные сами по себе имеют тип и привязываются к локации в памяти. Вы можете изменить значение в этой локации, но оно должно быть того же типа. Именно поэтому в статических языках нужно объявлять тип переменных. В Python этого делать не требуется, поскольку имя может ссылаться на все что угодно: значение и тип мы получаем, идя по цепочке к самому объекту с данными. Такой подход экономит время, но при этом имеет свои недостатки.
• Вы можете неверно написать имя переменной и получить исключение, поскольку она ни на что не ссылается, Python не выполняет такую проверку автоматически в отличие от статических языков. В главе 19 показывается способ предварительной проверки переменных.
• В сравнении с такими языками, как С, у Python скорость работы ниже. Ведь он заставляет компьютер выполнять больше работы, для того чтобы вам не пришлось выполнять ее самостоятельно.
Попробуйте сделать следующее с помощью интерактивного интерпретатора (рис. 2.4).
1. Как и раньше, присвойте значение 7 имени a. Это создаст объект-«ящик», содержащий целочисленное значение 7.
2. Выведите на экран а.
3. Присвойте имя а переменной b, заставив b прикрепиться к объекту-«ящику», содержащему значение 7.
4. Выведите b.
>>> a = 7
>>> print(a)
7
>>> b = a
>>> print(b)
7
|
|
|
Рис. 2.3. Имена указывают на объекты (переменная указывает на целочисленный объект со значением 7) |
| Рис. 2.4. Копирование имени (теперь переменная b указывает на тот же целочисленный объект) |
В Python, если нужно узнать тип какого-либо объекта (переменной или значения), можно использовать конструкцию type(объект). type() — одна из встроенных в Python функций. Чтобы проверить, указывает ли переменная на объект определенного типа, используйте конструкцию isinstance(type):
>>> type(7)
<class 'int'>
>>> type(7) == int
True
>>> isinstance(7, int)
True
Когда я упоминаю функцию, то после ее имени размещаю круглые скобки (()) и таким образом подчеркиваю, что это именно функция, а не имя переменной или что-либо еще.
Попробуем проделать это с разными значениями (58, 99.9, 'abc') и переменными (a, b):
>>> a = 7
>>> b = a
>>> type(a)
<class 'int'>
>>> type(b)
<class 'int'>
>>> type(58)
<class 'int'>
>>> type(99.9)
<class 'float'>
>>> type('abc')
<class 'str'>
Класс — это определение объекта (классы детально рассматриваются в главе 10). В Python значения терминов «класс» и «тип» примерно одинаковы.
Как вы могли заметить, при упоминании имени переменной Python ищет объект, на который она ссылается. Неявно Python выполняет большое количество действий и часто создает временные объекты, которые будут удалены спустя одну-две строки.
Снова рассмотрим пример, показанный ранее:
>>> y = 5
>>> x = 12 - y
>>> x
7
В этом фрагменте кода Python сделал следующее:
• создал целочисленный объект со значением 5;
• создал переменную у, которая указывает на этот объект;
• нарастил счетчик ссылок для объекта, содержащего значение 5;
• создал еще один целочисленный объект со значением 12;
• вычел значение объекта, на который указывает переменная у (5), из значения 12, содержащегося в анонимном объекте;
• присвоил результат (7) новому (пока еще безымянному) целочисленному объекту;
• заставил переменную х указывать на этот новый объект;
• нарастил счетчик ссылок для объекта, на который указывает переменная х;
• нашел значение объекта, на который ссылается переменная х (7), и вывел его на экран.
Когда количество ссылок на объект становится равным нулю, это означает, что ни одно имя на него больше не ссылается, поэтому хранить такой объект нет необходимости. В Python имеется сборщик мусора, который позволяет повторно использовать память, занятую уже ненужными на данный момент объектами: представьте себе, будто кто-то следит за этими полками с памятью и забирает ненужные коробки на переработку.
В нашем случае объекты со значениями 5, 12 и 7, а также переменные x и y больше не нужны. Сборщик мусора Python может или отправить их в небесный рай для объектов, или сохранить, исходя из соображений производительности, так как небольшие целые числа используются довольно часто.
Вы можете присвоить значение сразу нескольким переменным одновременно:
>>> two = deux = zwei = 2
>>> two
2
>>> deux
2
>>> zwei
2
Поскольку имена указывают на объекты, если изменить значение, присвоенное имени, оно начнет указывать на другой объект. Счетчик ссылок старого объекта уменьшится на 1, а счетчик ссылок нового увеличится на ту же величину.
Как вы видели на рис. 2.4, присваивание существующей переменной а новой переменной b заставит b указывать на тот же объект, что и a. Если вы выберете этикетку a или b и обратитесь к объекту, на который они указывают, вы получите одинаковый результат.
Если объект неизменяем (например, целое число), его значение нельзя изменить, поэтому по умолчанию оба имени являются доступными только для чтения. Попробуйте выполнить следующий код:
>>> x = 5
>>> x
5
>>> y = x
>>> y
5
>>> x = 29
>>> x
29
>>> y
5
Когда мы присваиваем переменную x переменной y, переменная y начинает указывать на целочисленный объект со значением 5, на который также указывает и переменная x. Далее мы изменяем переменную x так, чтобы она указывала на целочисленный объект со значением 29. Объект со значением 5, на который все еще указывает переменная y, не изменился.
В случае, когда оба имени указывают на изменяемый объект, вы можете изменить значение объекта с помощью любого имени. Если вы этого еще не знали, такая особенность может вас удивить.
Список представляет собой изменяемый массив значений (в главе 7 этот тип данных описывается более подробно). В нашем примере a и b указывают на список, содержащий три целочисленных объекта:
>>> a = [2, 4, 6]
>>> b = a
>>> a
[2, 4, 6]
>>> b
[2, 4, 6]
Эти элементы списка (a[0], a[1] и a[2]) сами по себе являются именами, указывающими на целочисленные объекты со значениями 2, 4 и 6. Список хранит элементы в заданном порядке.
Теперь давайте изменим первый элемент списка с помощью имени а и убедимся, что список b также изменился:
>>> a[0] = 99
>>> a
[99, 4, 6]
>>> b
[99, 4, 6]
Когда первый элемент списка изменяется, он больше не указывает на объект со значением 2. Теперь он указывает на объект со значением 99. Список все еще имеет тип list, но его значения (элементы списка и их порядок) можно изменить.
Он говорил правильные вещи, но называл их неверными именами.
Элизабет Барретт Браунинг
Удивительно, но выбор соответствующих имен для переменных очень важен. Во многих примерах кода, которые мы успели рассмотреть, я использовал простейшие имена вроде a и x. В реальных программах вам будет нужно отслеживать гораздо больше переменных одновременно и придется балансировать между краткостью и понятностью. Например, имя num_loons можно напечатать быстрее, чем number_of_loons или gaviidae_inventory, однако они более понятны, чем имя n.
Числа! Они такие увлекательные! Хотя вы, наверное, даже не предполагали насколько. Вы увидите, как использовать Python в качестве калькулятора, а также узнаете, как кот помог создать систему счисления.
2.1. Присвойте целочисленное значение 99 переменной prince и выведите ее на экран.
2.2. Какого типа значение 5?
2.3. Какого типа значение 2.0?
2.4. Какого типа выражение 5+2.0?
async и await появились в Python 3.7.
Или на Остров забытых объектов.
Цифра 8 похожа на снеговика!