Типизация в Python: Сильно, Динамично, Неявно
Все знают, что в Python типы данных делятся на изменяемые и неизменяемые, а как устроена сама типизация в Python? Чтобы ответить на этот вопрос, надо рассмотреть какими характеристиками обладает система типов Python.
Обычно, когда описывают систему типов языка программирования, отвечают на 3 вопроса:
- Выполняет ли язык неявные автоматические преобразования типов?
- На каком этапе выясняется тип переменной?
- Надо ли явно указывать тип переменных?
Отвечая на эти вопросы, можно узнать имеет ли язык сильную или слабую типизацию, статическую или динамическую, явную или неявную.
Python имеет сильную динамическую неявную типизацию и давайте подробнее рассмотрим почему.
1. Сильная типизация
Когда мы говорим о "сильной типизации" в контексте программирования, мы имеем в виду строгий подход языка к обработке переменных разных типов. В языке программирования Python, который отличается сильной типизацией, разные типы данных не смешиваются автоматически. Так, выражение "some string" - 3
вызовет ошибку, потому что язык не позволяет неявно преобразовывать строку в число для выполнения математической операции. При ошибках связанных с типами Python генерирует исключение TypeError.
Аналогично, попытка сложить список [2, 1, 0]
и множество set([2, 23, 2])
также приведет к ошибке, поскольку Python не будет искать способы автоматически преобразовать одну структуру данных в другую для выполнения операции. В противопоставление можно привести в пример язык JavaScript, который позволяет без проблем складывать строки с числами: 3 + '1' // Получится строка: '31'
.
Несмотря на сильную типизацию, Python допускает некоторые операции между различными типами данных, но это объясняется явной реализацией, а не автоматическим преобразованием:
# Повторение последовательностей: # вы можете "умножить" строку или список на число, # и это даст повторяющуюся последовательность. print("word" * 3) # wordwordword print([1, 2] * 3) # [1, 2, 1, 2, 1, 2] # float и int print(5 + 0.1) # 5.1 # bool и число print(2.2 + True) # 3.2 # Замечание: тип bool наследуется от int, # и здесь операция сложения не вызывает сомнений. # и другие...
Эти операции возможны благодаря внутренним механизмам языка, предоставляющим четкую реализацию для таких случаев. Например, вы в своей программе можете явно задать поведение вашего типа (класса) при сложении с другим объектом, используя магические методы. Но если вы этого не сделаете, то получите ошибку при попытке сложить с чем-то.
Для наглядности сделаем так, чтобы строка работала как в JavaScript: приводила складываемый объект к строке и конкатенировала результат.
class JavaScriptStr(str): def __add__(self, value): # Явная реализация сложения return super().__add__(str(value)) number = JavaScriptStr("3") print(number + 1) # "31"
2. Динамическая типизация
Динамическая типизация Python означает, что типы данных переменных определяются во время выполнения программы, а не на этапе компиляции, как в языках со статической типизацией. Это облегчает написание гибкого кода и позволяет, например, создавать функции, которые работают с различными типами данных.
Рассмотрим функцию find
, которая ищет элемент required_element
в последовательности sequence
. В языке C для реализации этой же логики пришлось бы написать несколько версий функции для разных типов данных, тогда как в Python достаточно одной:
def find(required_element, sequence): """Осуществляет поиск элемента в последовательности.""" for index, element in enumerate(sequence): if element == required_element: return index return -1 print(find(2, [1, 2, 3])) # Выведет: 1 print(find("c", ("a", "b", "c", "d"))) # Выведет: 2 print(find(1, 1)) # Возникнет исключение TypeError
Негативная сторона динамической типизации в том, что она может порождать неожиданные ошибки во время выполнения программы. Например, если мы в sequence
передадим не итерируемый объект (по которому нельзя пройтись с помощью for), то получим ошибку. Но узнаем о ней, только когда выполнение программы дойдёт до конкретной строчки с for обходом по этому объекту.
Также динамическая типизация позволяет перезаписывать в одну переменную данные разных типов.
number = 1 # int number = "One" # str
3. Неявная типизация
В языках с неявной типизацией не требуется явно указывать тип переменной — интерпретатор сам определяет тип на основе присвоенного значения. Это упрощает код и делает его более читаемым:
var = 5 # int var = "some string" # теперь var - это str
Аннотация Типов в Python
Начиная с версии 3.5, в Python появилась поддержка аннотации типов, что позволяет программистам явно указывать типы переменных и возвращаемых функцией значений. Это не влияет на работу интерпретатора, но помогает в программировании и поддержке кода:
def plus(x: int, y: int) -> int: return x + y print(plus(5, 10)) # Выведет: 15 # Функция выполнит конкатенацию строк. # Это показывает, что аннотация не равно явная типизация. print(plus("Hello ", "world!")) # Выведет: Hello world!
Указание типов через аннотации делает код более понятным для других разработчиков, а также позволяет находить ошибки до выполнения программы, используя дополнительные инструменты статического анализа. Но язык по-прежнему сохраняет гибкость, присущую динамической типизации
Заключение
Типизация в Python сочетает в себе строгость и гибкость: с одной стороны, сильная типизация защищает от ошибок, связанных с несоответствием типов, с другой — динамическая типизация позволяет писать более универсальный и легко читаемый код. Аннотации типов приносят ясность в структуру программы, делая ее более предсказуемой и безопасной.