Что такое динамическая типизация и как она трактуется по Лутцу?

Друзья, всем привет. Продолжаю медитировать над книгой «Изучаем Python». Сегодня речь зайдёт про сущность динамической типизации. Эта статья будет иметь более теоритическое, ежели практическое значение, однако этот вопрос может легко попасться на собеседовании. Поэтому читаем, вникаем и думаем

Итак какова сущность динамической типизации?

Рассмотрим пару примеров. Допустим, у нас имеются следующие переменные:

intNumber = 6; floatNumber = 3.5 ; StringRow = 'abba'; BoolValue = True print(type(intNumber), type(floatNumber), type(StringRow), type(BoolValue)) # <class 'int'> <class 'float'> <class 'str'> <class 'bool'>

Как Python определил, что данные значения соответствуют определённому типу?

А всё дело в том, что определение типов в Python происходит автоматически, и как раз за это и отвечает метод динамической типизации.

Должен признаться, существует некая группа программистов, которая не любит работать с динамически типизированными объектами. Как правило это уже продвинутые программисты, которые умеют программировать на нескольких языках и в их представлении Python является даже не языком программирования. Поэтому будьте осторожны, когда начинаете спор с Java программистом или C++ программистом. У них мозг работает несколько иначе, чем у тех людей, кто программирует на Python.

Итак, из чего состоит программа в Python?

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

Переменные - это записи в системной таблицы, в которых предусмотрены места для связей с объектами

Объекты - это области выделенной памяти с достаточным пространством для представления значений, для которых они предназначены

Ссылки - это указатели от переменных к объектам

Для ассоциации в книге приводится следующая схема. Я её немного поменял, но совсем чуть-чуть. Она описывает задачу присвоения переменной intNumber значения 6 (взял это из выдуманного примера, который описал выше)

Что такое динамическая типизация и как она трактуется по Лутцу?

Что происходит, когда мы создаём переменную?

1) Сначала создаётся объект 6

2) Затем создаётся переменная с именем intNumber

3) Создаётся ссылка, связывающая объект 6 с переменной intNumber

Каждый объект имеет сложную структуру, чем описывается на рисунке. Вместе с вышеизложенным он делится на два стандартных поля: обозначение типа и счётчик ссылок. Для наглядности сделаю для описания умную карту.

Что такое динамическая типизация и как она трактуется по Лутцу?

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

1) Имена не имеют типов

2) Типы обитают в объектах

3) Объектам известно какого типа он содержит переменную

Что необходимо знать про объекты и сборку мусора?

Так-так, тут уже появляется новый термин, и при чём очень важный. Следует правильно понимать, что под ним люди имеют ввиду, чтобы они вас смогли понять.

Итак, Python - очень оптимизированный язык программирования. И основное в этом преимущество состоит в том, что ненужные объекты он убирает из памяти. Вот пример:

stringName = 'Misha' stringName = 'Vasya'

Как можно видеть из примера, у нас одинаковые имена stringName. И если мы выведем stringName методом print. То получим объект Vasya

print(stringName) #Vasya

А что случилось с Misha? Правильно, он уничтожился. Таким образом произошло освобождение памяти, а за это отвечает сборщик мусора.

Сборщик мусора также используется с объектами разными типами. Таким образом вот следующий пример:

x = 42 # тип int x = 'shrubbery' # тип string x = 3.14 # тип float x = [1,2,3,4] # тип list

Таким образом при выводе переменной x происходит вывод последнего объекта, а другие удаляются.

print(x) # [1, 2, 3, 4]

Сборщик мусора основан на счётчике ссылок (картинка 2), однако они имеют компоненты, обнаруживающие и освобождающие пространство в памяти для объектов с циклическими ссылками

По Лутцу циклические ссылки служат основной проблемой сборщика мусора, основанных на счётчиках ссылок.

А что такое циклические ссылки?

Циклические ссылки возникают тогда, когда объект ссылается на самого себя. Вот пример:

x = [1,2,3,4] # тип list x.append(x) print(x) #[1, 2, 3, 4, [...]]

Если честно, я так никогда не делал, да и Мартин не рекомендует. Однако вывод весьма любознательный на мой взгляд. Вполне возможно резонно сказать на собеседовании, что подобный случай является редким, однако «с ними нужно обходиться особым образом», поскольку счётчики ссылок никогда не уменьшаются до 0

Для получения дополнительных сведений автор рекомендует изучить модуль gc. Я о нём узнал только недавно, поэтому до сей поры не игрался.

Что такое разделяемые ссылки?

Автор приводит показательный пример:

name = 'Misha' newName = name

Итак, я вижу переменную name и вижу новую переменную newName. То есть когда мы объявили переменную newName, то этим самым мы организовали ссылку на один и тот же объект Misha

Для иллюстрации автор предлагает нам запомнить тему с помощью простой схемы.

Что такое динамическая типизация и как она трактуется по Лутцу?

Иллюстрация демонстрирует как сценарий с множеством имён ссылается на на тот же самый объект. Такой процесс называется разделяемой ссылкой

А теперь добавим новую переменную как в следующем примере

Что же происходит здесь?

Что такое динамическая типизация и как она трактуется по Лутцу?

Эту схему уже понимать сложнее. Однако на основе неё можно сделать следующий вывод:

1) Переменные в Python всегда являются указателями на объекты,

2) Установка переменной в новое значение не изменяет первоначальный объект, но в замен указывает на ссылку переменной, находящейся в другом объекте

Как разделяемые ссылки меняют объекты?

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

Автор напоминает, что изменяемыми типами данных являются списки, словари и множества

Для того, чтобы продемонстрировать процесс изменения для изменения объектов, автор предлагает это сделать на примере следующего кода:

L1 = [2,3,4] L2 = L1 L1[0] = 24 print(L1 , L2) # [24, 3, 4] [24, 3, 4]

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

А теперь, внимание, вопрос!

Как изменить данные в списке таким образом, чтобы объекты в двух списках не менялись?

Для ответа на этот вопрос, Лутц показывает следующий пример:

L1 = [2,3,4] L2 = L1[:] L1[0] = 24 print(L1 , L2) # [24, 3, 4] [2, 3, 4]

Что делает ":" ?

Фактически это оператор делает копию данных переменной L1. Фактически, получается, что данный объект сохраняется в памяти и L2 уже является новым объектом.

Аналогичные процессы создаются при вызове оператора copy() и deepcopy()

Оператор copy() создаёт поверхностное копирование объекта, а deepcopy() - глубокое копирование.

Предлагаю потренироваться и проследить поведение представленных методов. Не всё же мне писать)

Как отличить разделяемые ссылки от оператора сравнения?

Эта тема очень важная, поскольку здесь определяется суть оператора is в Python. На тему использования is даже сделали небольшой гайд на YouTube.

Примерно об этом же говорит соответствующая глава в книге

Что ещё важно знать про Динамическую типизацию?

1) Динамическая типизация - абстрактное пониятие

2) В Python абсолютно всё взаимодействует через присваивание и ссылки

3) Для углубления понимания. Динамическая типизация является корнем полиморфизма

4) В Python существует только одна модель присваивания

Вместе с разделяемыми ссылками в Python есть и «слабые ссылки», что это такое?

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

Слабая ссылка - довольно развитый инструмент, который связан с моделью ссылок, но операции is будут без них не понятны

1) Данный термин реализуется в стандартной библиотеке weakref . Она представляет ссылку на объект, которая не препятствует выполнению сборки мусора в сборке мусора

2) Слабые ссылки удобны в кешах для крупных объектов на основе словарей: без них них одна ссылка приводила бы к хранению объекта в памяти бесконечно долго.

На практике я не использовал «слабые ссылки», поэтому их применение лично для меня остаётся открытым вопросом.

На этом всё! До новых встреч :)

3 комментария

Не очень понятен посыл статьи.

Их различие следует вызубрить на зубок, <...>. Их не обязательно знать дословно

Хм...

1

То есть Главное понимать смысл, но заучивать не обязательно

А смысл статьи - рефлексия над материалом)