Как использовать статическую типизацию в Python.

Одной из распространённых проблем в Python являются ошибки связанные с динамической типизацией. 

Давайте рассмотрим пример такой ошибки:

def get_first_name(full_name):
    return full_name.split(" ")[0]

fallback_name = {
    "first_name": "UserFirstName",
    "last_name": "UserLastName"
}

raw_name = input("Please enter your name: ")
first_name = get_first_name(raw_name)

# If the user didn't type anything in, use the fallback name
if not first_name:
    first_name = get_first_name(fallback_name)

print(f"Hi, {first_name}!")

Всё что мы здесь делаем, это запрашиваем имя пользователя, а затем распечатываем его "Hi <name>!". И если пользователь ничего не вводит, то мы хотим вывести на печать "Hi UserFirstName!". Эта программа отлично сработает если пользователь введёт имя, но если пользователь ничего не введёт то мы получим:

Traceback (most recent call last):
  File "D:/work/learning/test.py", line 14, in <module>
    first_name = get_first_name(fallback_name)
  File "D:/work/learning/test.py", line 2, in get_first_name
    return full_name.split(" ")[0]
AttributeError: 'dict' object has no attribute 'split'

Проблема в том что fallback_name это не строка а словарь. И когда мы вызываем get_first_name мы получаем ошибку потому, что в словаре нет функции split

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

Статическая типизация предотвратит эту ошибку, прежде чем вы запустите программу. И скажет вам, что функция  get_first_name переданным аргументом ожидает видеть строчку а не словарь. Редактор кода подсветит её как ошибку.

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

from typing import Dict


def get_first_name(full_name: str) -> str:
    return full_name.split(" ")[0]

fallback_name: Dict[str, str] = {
    "first_name": "UserFirstName",
    "last_name": "UserLastName"
}

raw_name: str = input("Please enter your name: ")
first_name: str = get_first_name(raw_name)

# If the user didn't type anything in, use the fallback name
if not first_name:
    first_name = get_first_name(fallback_name)

print(f"Hi, {first_name}!")

В Python 3.6 мы можем объявлять типизированные переменные так:

wariable_name: type

Если мы хотим сразу инициализировать переменную то можно написать так:

my_string: str = "My String Value"

Так мы объявляем типы в функции:

def function_name(arg1: type, arg2: type) -> teturn_type:

И если вы используете IDE, такую как PyCharm, она вам подскажет ошибку до того как вы запустите программу на исполнение.

Рассмотрим другие примеры синтаксиса статической типизации в Python 3.6

Объявление переменных str или int происходит достаточно легко. А как объявлять сложные типы данных, такие как словари, списки и кортежи. Давайте рассмотрим на примере:

from typing import Dict, List, Tuple

name_counts: Dict[str, int] = {
    "Adam": 10,
    "Guido": 12
}

numbers: List[int] = [1, 2, 3, 4, 5, 6, 7]

list_of_dicts: List[Dict[str, int]] = [
    {"key1": 1},
    {"key2": 2}
]

my_data: Tuple[str, int, float] = ("Adam", 10, 5.7)

Можно создавать псевдонимы для сложных типов данных просто назначая им новое имя:

from typing import List, Tuple

LatLngVector = List[Tuple[float, float]]

points: LatLngVector = [
    (25.91375, -60.15503),
    (-11.01983, -166,484877),
    (-11.01983, -166.48477)
]

Иногда может потребоваться чтобы ваши функции принимали несколько типов данных, тогда можно воспользоваться типом Union. Можно использовать тип Any, чтобы ваша функция принимала любой тип.

Отлично! Но стоит ли использовать во всех моих программах этот новый синтаксис?

Этот синтаксис очень новый в Python. И полностью он поддерживается начиная с версии 3.6. Так что сейчас наверное рановато использовать это в проектах, но для новых своих проектов где вы можете выкатить минимальные требования для Python не ниже версии 3.6, это может быть очень полезно. Так же можно легко смешивать код с динамической типизацией. Язык развивается и это очень радует.