- Динамическая типизация#
- Статическая типизация в C++ #
- // 1#
- // 2#
- // 3#
- // 4#
- Итого#
- Динамическая типизация в python #
- # 1#
- Что такое динамическая типизация и как она трактуется по Лутцу?
- Типы данных в Python для начинающих: какие бывают и как с ними работать
- Что такое строгая динамическая типизация
- Изменяемые и неизменяемые типы данных
- Строки
- Списки
- Кортежи
- Что почитать про типы данных в Python
- Курс
Динамическая типизация#
Кажется, что переменная a меняет свой тип 3 раза. Как это возможно? Прежде чем ответить на этот вопрос, освежим в памяти, как устроена типизация в C/C++ , и разберемся, почему это изначально может смущать.
Функция type спрашивает у объекта его тип.
Статическая типизация в C++ #
Чтобы разобрать, как устроена типизация в C/C++ , рассмотрим следующий код на языке C/C++ .
#include #define FIRSTBYTE 1 #define SECONDBYTE 256 #define THIRDBYTE 256*256 #define FOURTHBYTE 256*256*256 int main() // 1 int a = 65*FIRSTBYTE + 66*SECONDBYTE + 67*THIRDBYTE + 68*FOURTHBYTE; std::cout <a <std::endl; // 1145258561 // 2 char *b = reinterpret_castchar*>(&a); // 3 for(size_t i=0; i4; ++i) std::cout <b[i]; // ABCD std::cout <std::endl; // 4 a = 'A'; std::cout <a; // 65 return 0; >
Если его скомпилировать и запустить, то вы должны получить следующее в стандартном потоке вывода.
Приведенный выше код опирается на предположение, что в системе целочисленный тип int занимает 4 байта, что не гарантируется стандартами C++ .
Разберем по шагам, что происходит в этой программе.
// 1#
В начале объявляется целочисленная переменная a типа int , которая инициализируется значением \(65 \cdot 256^0 + 66 \cdot 256^1 + 67 \cdot 256^ + 68 \cdot 256^ = 1145258561\) .
// 1 int a = 65*FIRSTBYTE + 66*SECONDBYTE + 67*THIRDBYTE + 68*FOURTHBYTE; std::cout a std::endl; // 1145258561
В момент объявления выделяется достаточная для хранения переменной типа int (4 байта на большинстве платформ) область оперативной памяти, и переменная a связывается с этой областью памяти. Во момент инициализации в эту область памяти записывается число 1145258561. Это число намерено задано в разложении по степеням числа \(256\) , чтобы явно выделить байты этого числа: 65, 66, 67 и 68 (см. статью в википедии про порядок байтов, если непонятно о чем идёт речь).
// 2#
Далее объявляется указатель b на символьную переменную типа char , который инициализируется адресом памяти переменной a .
// 2 char *b = reinterpret_castchar*>(&a);
Чтобы провернуть такой трюк, необходимо сначала поменять тип указателя, т.к. переменная a имеет 32-битный целочисленный тип int , а переменная b является указателем 8-битного типа char (который формально тоже является целочисленным). Компилятор запретил бы такую операцию из-за несоответствия типов (и невозможности их приведения друг к другу безопасным образом), поэтому в коде используется reinterpret_cast, который вынуждает компилятор привести тип указателя.
Такие возможности языка C/C++ приводятся здесь только с целью демонстрации механизма работы переменных в C/C++ . В настоящих программах рекомендуется избегать таких приёмов.
// 3#
Далее в цикле выводятся значения b[0] , b[1] , b[2] и b[3] .
// 3 for(size_t i=0; i4; ++i) std::cout <b[i]; std::cout <std::endl;
В результате видим строку ABCD . Чтобы разобраться с тем, как это произошло, необходимо учесть
- b — указатель, который хранит в себе адрес переменной a ;
- однако b — указатель 8-битного типа, а значит при его разыменовании ( b[0] , что то же самое, что и *b ) захватывается область размером всего в 1 байт.
- b[i] эквивалентно *(b + i) , т.е. то же самое, что и b[0] , но на i байт правее.
- т.к. в и тоге выводится символьная переменная, то она выводится согласно таблице ASCII, в которой 65 соответствует символу “ A ”, 66 — символу “ B ”, 67 — символу “ C ”, 68 — символу “ D ”.
Следующий рисунок иллюстрирует примерное устройство памяти в этой программе на этот момент времени.
// 4#
Далее в переменную a записывается значение “ ‘A’ ”;
Из-за того, что C/C++ — статически типизированный язык, то компилятор знает, что a — переменная типа int (с таким типом она была объявлена), и A — значение типа char , которое может быть без потерь приведено к int (8 бит меньше 32 бит). Исходя из этой информации, компилятор допускает эту операцию, неявно приводя символьное значение ‘A’ к целочисленному значению 65 и записывая в соответствующую переменной a область памяти это значение.
Итого#
Итого, опуская технические подробности:
- переменная соответствует области в памяти;
- у переменной всегда есть определенный тип, он указывается при объявлении переменной и известен на этапе компиляции;
- в самой области памяти нет информации о том, как правильно прочитать последовательность бит, которую она содержит;
- при обращении к переменной, её значение получается в результате интерпретации последовательности бит в соответствующей области памяти на основании типа этой переменной;
- технически возможно прочитать одну и ту же область памяти по-разному, обращаясь к ней переменными разных типов;
- оператор присвоения “ = ” записывает значение справа от него в область памяти, именованную переменной слева от него с предварительной проверкой совместимости типов.
Динамическая типизация в python #
В python тип хранится не в переменной, а в самом объекте, а сама переменная всего лишь ссылается на объект (в python всё объекты), ничего не подозревая о типе этого самого объекта. На самом деле переменная в CPython представляет собой обертку над указателем C типа void , который всегда автоматически разыменовывается, а оператор присвоения “ = ” связывает переменную слева от него с объектом справа от него (изменяет значение указателя).
В связи с этим, иногда термин “переменная” (variable) избегается в отношении python и заменяется терминами “имя” (name), “ссылка” (reference) или идентификатор.
В python работать с адресами памяти напрямую невозможно. Единственное исключение составляет функция id , которая в некоторых реализациях возвращает адрес памяти (целое число), где хранится объект. Эта функция нужна только для того, чтобы всегда можно было выяснить, ссылаются ли два имени на один и тот же объект. Обратится по этому адресу средствами python невозможно.
Вернемся к примеру из начала раздела и объясним, как он работает.
a = 0 # a - переменная типа int print(a, type(a)) a = 0. # теперь a - переменная типа float print(a, type(a)) a = "zero" # теперь a - переменная типа str print(a, type(a)) a = [0, 0., "zero"] # теперь a - переменная типа list print(a, type(a))
# 1#
- Сначала вычисляется значение выражения справа: создается объект целочисленного типа int содержащий в себе значение 0 .
- Так как имени a до этого объявлено не было, то такое имя создаётся и связывается с объектом справа.
a = 0 # a - переменная типа int print(a, type(a))
Что такое динамическая типизация и как она трактуется по Лутцу?
Друзья, всем привет. Продолжаю медитировать над книгой «Изучаем Python». Сегодня речь зайдёт про сущность динамической типизации. Эта статья будет иметь более теоритическое, ежели практическое значение, однако этот вопрос может легко попасться на собеседовании. Поэтому читаем, вникаем и думаем
Рассмотрим пару примеров. Допустим, у нас имеются следующие переменные:
intNumber = 6; floatNumber = 3.5 ; StringRow = ‘abba’; BoolValue = True print(type(intNumber), type(floatNumber), type(StringRow), type(BoolValue)) #
Как Python определил, что данные значения соответствуют определённому типу?
А всё дело в том, что определение типов в Python происходит автоматически, и как раз за это и отвечает метод динамической типизации.
Должен признаться, существует некая группа программистов, которая не любит работать с динамически типизированными объектами. Как правило это уже продвинутые программисты, которые умеют программировать на нескольких языках и в их представлении Python является даже не языком программирования. Поэтому будьте осторожны, когда начинаете спор с Java программистом или C++ программистом. У них мозг работает несколько иначе, чем у тех людей, кто программирует на Python.
По Лутцу это: переменные, объекты и ссылки. Их различие следует вызубрить на зубок, ибо это может быть очередным ответом на вопрос. Их не обязательно знать дословно, главное дать внятное объяснение и понимать различия
Переменные — это записи в системной таблицы, в которых предусмотрены места для связей с объектами
Объекты — это области выделенной памяти с достаточным пространством для представления значений, для которых они предназначены
Ссылки — это указатели от переменных к объектам
Для ассоциации в книге приводится следующая схема. Я её немного поменял, но совсем чуть-чуть. Она описывает задачу присвоения переменной intNumber значения 6 (взял это из выдуманного примера, который описал выше)
Типы данных в Python для начинающих: какие бывают и как с ними работать
Готовимся к собеседованию на должность Python-разработчика. Знакомимся с системой типов в Python, встроенными типами данных и правилами работы с ними.
Иллюстрация: Оля Ежак для Skillbox Media
Пишет об истории IT, разработке и советской кибернетике. Знает Python, JavaScript и немного C++, но предпочитает писать на русском.
Python — объектно-ориентированный язык программирования, его основу составляют объекты и классы. Объект — это область памяти компьютера, которая описывается типом (он же класс) и значением. При этом от типа зависит область значений объекта, операции и методы, которые к нему можно применять.
Python предоставляет богатый набор встроенных типов данных. Поэтому при решении стандартных задач питонист реже пишет собственные классы, чем, например, разработчик на Java.
Из этой статьи вы узнаете:
Что такое строгая динамическая типизация
Python — язык программирования со строгой динамической типизацией.
«Строгая» означает, что язык не производит неявные преобразования типов и не создаёт сюрпризов при их случайном смешении.
Чтобы понять, о чём идёт речь, запустите этот код на Python и JavaScript, а затем сравните результаты:
Изменяемые и неизменяемые типы данных
Типы данных в Python можно разделить на изменяемые и неизменяемые.
Когда мы присваиваем новое значение неизменяемому объекту, Python не перезаписывает его, а создаёт новый объект с тем же именем. Чтобы в этом убедиться, достаточно проверить id — уникальный номер, который присваивается каждому объекту в Python:
Строки
Строки (string) — это последовательности символов, поэтому к ним применимы многие методы других последовательностей: списков и кортежей. Например, обращение к элементу по индексу, вычисление количества символов, конкатенация и получение среза.
Рассмотрим основные операции со строками в Python:
Списки
Список (list) — это упорядоченная коллекция объектов. Списки могут иметь сколько угодно уровней вложенности и хранить неограниченное количество объектов. Кроме того, в одном списке могут одновременно храниться объекты разных типов.
Над списками можно производить те же операции, что и над строками:
Кортежи
Кортежи (tuple) — это те же списки, только неизменяемые. Над ними можно производить те же операции, что и над списками, — кроме тех, которые изменяют кортеж:
Что почитать про типы данных в Python
Теперь вы знаете о типах в Python достаточно, чтобы решать простейшие задачи и даже ответить на вопросы на собеседовании. Если хотите узнать о системе типов ещё больше — почитайте классические источники:
- «Изучаем Python 3», Часть II. Типы и операции, М. Лутц;
- «Программирование на Python 3», Глава 2. Типы данных, М. Саммерфилд;
- документацию Python, раздел «Built-in Types».
Больше интересного про код в нашем телеграм-канале. Подписывайтесь!
Читайте также:
Числа, состоящие из действительной и мнимой части. Применяются в теории колебаний, квантовой механике и при обработке сигналов.
Курс
Python просто выучить, даже если вы никогда не программировали. Во время обучения вам будет помогать эксперт-куратор. Вы разработаете 3 проекта для портфолио, а Центр карьеры поможет найти работу Python-разработчиком.
Профессии с трудоустройством