Динамическая типизация в Python

Опубликован: 21.08.2023 161

Python — объектно-ориентированный язык программирования со строгой динамической типизацией.

Это значит, что типы объектов определяются "динамически" в процессе исполнения программы или "на лету". Поэтому при написании кода можно не указывать типы переменных (как это требуется в языках со "статической" типизацией: C++, Java, Pascal и др.).

При этом, Python относится к языкам со строгой типизацией. Это означает, что различные типы данных нельзя смешивать в одном выражении (например, сложение целого числа int со строкой str вызовет исключение TypeError):

2 + '2'
# Traceback (most recent call last):
#   File "C:\main.py", line 1, in <module>
#     2 + '2'
#     ~~^~~~~
# TypeError: unsupported operand type(s) for +: 'int' and 'str'

Если бы Python был языком со слабой типизацией, результатом выполнения такого кода стало бы 22. В ситуации, когда смешиваются разные типы, слабо типизированные языки могут неявно приводить значения к одному из них. Иногда это вызывает непредсказуемые последствия, например, если по неосторожности использовать строку с цифрами вместо числа. Однако Python допускает подобное в некоторых случаях:

2 + 2.2
4.2

Типы int и float могут свободно взаимодействовать. Это продиктовано удобством и естественностью таких преобразований.

Неявная типизация

Неявная типизация Python подразумевает возможность создавать объекты, не указывая их тип:

a = 1  # создание объекта типа int
print(a, type(a))
1 <class 'int'>

b = 1.1  # создание объекта типа float
print(b, type(b))
1.1 <class 'float'>

c = 'a'  # создание объекта типа string
print(c, type(c))
'a' <class 'string'>

Если бы в Python была явная типизация, приходилось бы каждый раз указывать тип любой переменной.

Неявная типизация в Python придает языку дополнительную гибкость и позволяет менять тип переменной (или последовательности) "на лету" - просто изменяя ссылку на объект другого типа данных. Тип данных хранится не в переменной, а в самом объекте, а переменная всего лишь ссылается на объект, ничего не подозревая о типе этого самого объекта.

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

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

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

Таким образом, в Python (опуская технические подробности):

  • вместо переменных - имена со ссылками на объекты определенного типа;
  • у имени (т.е. переменной) нет типа;
  • имя всегда ссылается на какой-то объект, внутри которого кроме полезных данных хранится служебная информация, включая тип объекта;

Оператор присваивания = связывает имя слева от него с объектом справа от него:

  • если такого имени ещё нет, то оно создаётся;
  • если такое имя уже существует (связанно с каким-то объектом), то ссылка перекидывается на новый объект.

Утиная типизация

В Python применяется так называемая утиная типизация:

Если оно выглядит как утка, плавает как утка и крякает как утка, то, вероятно, это утка.

Это означает, что тип данных не имеет значения — важно лишь то, какие методы и свойства они поддерживают.

Например, чтобы узнать, длину объекта, можно использовать функцию len(), которая не проверяет, к какому типу относится объект, а всего лишь обращается к магическому методу __len__(). Можно узнать длину любого объекта, у которого прописан метод __len()__ (не важно, как именно).

И наоборот, объект с очевидной длиной, но без метода __len__() нельзя обработать этой функцией.

class SomeClass:
    length = 12

    def __len__(self):
        return self.length


len('123')  # длина строки
3
len([1, 2])  # длина списка
2
len(SomeClass())  # длина класса
2
len(123)  # попытка получить длину целого числа вызовет исключение TypeError
# Traceback (most recent call last):
#   File "C:\main.py", line 13, in <module>
#     len(123)
# TypeError: object of type 'int' has no len()

Заключение

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

Похожие посты

Использование аннотации типов в Python

Изменяемые и неизменяемые типы данных

Встроенные типы данных в Python

Преобразование типов данных в Python

Комментариев нет.