Alias для типа

После нашего отступления о структурах продолжим накидывать каркас приложения.

В coordinates.py оставим структуру dataclass с параметрами slots и frozen, потому что не предусматривается изменение координат, которые вернёт нам GPS-датчик на ноутбуке.

from dataclasses import dataclass

@dataclass(slots=True, frozen=True)
class Coordinates:
    longitude: float
    latitude: float

def get_gps_coordinates() -> Coordinates:
    return Coordinates(longitude=10, latitude=20)

Возвращаемое значение вставили пока, просто чтобы не ругались проверки в редакторе. Потом напишем реализацию, которая запросит координаты у команды whereami, распарсит её результаты и вернёт как результат функции get_gps_coordinates.

Составим weather_api_service.py:

from coordinates import Coordinates

def get_weather(coordinates: Coordinates):
    """Requests weather in OpenWeather API and returns it"""
    pass

Так, какой тип у погоды будет возвращаться? Тут главное не смотреть на формат данных в API-сервисе, потому что сервис и формат данных в нём вторичны, первичны наши потребности. Какие данные нам нужны? Нам нужна температура за бортом, наше место, общая характеристика погоды — ясно/неясно/снег/дождь и т. п., а также мне лично ещё интересно, во сколько сегодня восход солнца и закат солнца. Вот эти данные нам нужны, их пусть функция get_weather и возвращает. В каком формате?

Так, ну давайте думать. Просто tuple? Точно нет. Вообще есть мнение, что если мы хотим использовать tuple, то стоит использовать NamedTuple, потому что в нём явно данные будут названы. Поэтому возможно NamedTuple.

Просто dict? Точно нет. Не будет нормальных проверок в IDE и статическом анализаторе, не будет подсказок, и читателю кода непонятно, что там внутри словаря. TypedDict? Лучше, но мне нравится доставать данные как атрибуты класса, а не как ключи словаря. Поэтому TypedDict тоже не будем брать.

Может dataclass? Можно.

Итого NamedTuple или dataclass? Оба варианта ок, можно выбрать любой вариант, я, пожалуй, тут выберу dataclass с параметрами frozen и slots просто потому что распаковывать структуру как кортеж нам незачем, а по памяти dataclass с такими параметрами даже эффективнее кортежа.

from dataclasses import dataclass
from datetime import datetime
from typing import TypeAlias

from coordinates import Coordinates

Celsius: TypeAlias = int

@dataclass(slots=True, frozen=True)
class Weather:
    temperature: Celsius
    weather_type: str  # Подумаем, как хранить описание погоды
    sunrise: datetime
    sunset: datetime
    city: str

def get_weather(coordinates: Coordinates):
    """Requests weather in OpenWeather API and returns it"""
    pass

Обратите внимание, как я обошёлся с температурой. Можно было прописать тип напрямую int, но я сделал alias, то есть псевдоним, для int с названием Celsius и теперь понятно, что у нас температура тут будет именно в градусах Цельсия, а не Фаренгейта или Кельвина. Тип TypeAlias как раз предназначен для указания таких алиасов типов.

Также, если какая-то функция будет принимать на вход или возвращать температуру, то мы тоже укажем для температуры там конкретный тип Celsius, а не общий непонятный int.