├── README.md
├── eng-docs
└── assets
│ ├── back.png
│ ├── main.png
│ └── next.png
└── ru-docs
├── Stub_файлы.md
├── assets
├── back.png
├── main.png
└── next.png
├── Базовые_типы_данных.md
├── Введение_в_типизацию.md
├── Интеграция_mypy.md
├── Классы_и_типизация.md
├── Коллекции_и_их_типизация.md
├── Типизация_с_использованием_TypeVar.md
├── Типы_Any_и_NoReturn.md
├── Типы_объединений_и_опциональные_типы.md
├── Типы_протоколов.md
├── Типы_псевдонимов.md
└── Функции_и_типизация.md
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Содержание
4 | ## Модуль 1: Основы типизации в Python
5 | * **[1.1 Введение в типизацию: зачем, когда и как?](./ru-docs/Введение_в_типизацию.md)**
6 | * Динамическая и статическая типизация: плюсы и минусы
7 | * Типизация в Python: история и эволюция
8 | * Инструменты для статической типизации: mypy, PyCharm, другие
9 | * **[1.2 Базовые типы данных:](./ru-docs/Базовые_типы_данных.md)**
10 | * int, float, str, bool: основы работы с типами
11 | * None: отсутствие значения и его особенности
12 | * Duck typing: неявная типизация в Python
13 | * **[1.3 Коллекции и их типизация:](./ru-docs/Коллекции_и_их_типизация.md)**
14 | * list, tuple, dict, set: работа с коллекциями
15 | * Типизация коллекций: List\[int\], Tuple\[str, int\], Dict\[str, float\]
16 | * Генераторы и итераторы: типизация и особенности
17 | * **[1.4 Функции и типизация:](./ru-docs/Функции_и_типизация.md)**
18 | * Аннотация типов для аргументов и возвращаемого значения
19 | * Перегрузка функций и типы
20 | * Callable: работа с функциональными объектами
21 | * **[1.5 Классы и типизация:](./ru-docs/Классы_и_типизация.md)**
22 | * Аннотация типов для атрибутов класса
23 | * Методы класса и типизация
24 | * Наследование и типизация: как типы взаимодействуют с наследованием
25 |
26 | ## Модуль 2: Продвинутая типизация
27 | * **[2.1 Типизация с ипользованием TypeVar:](./ru-docs/Типизация_с_использованием_TypeVar.md)**
28 | * Создание универсальных функций и классов
29 | * Ограничение типов с помощью TypeVar
30 | * Практические примеры использования TypeVar
31 | * **[2.2 Типы объединений и опциональные типы:](./ru-docs/Типы_объединений_и_опциональные_типы.md)**
32 | * Работа с переменными, которые могут иметь разные типы
33 | * Использование Optional для обозначения возможности отсутствия значения
34 | * **[2.3 Типы псевдонимов:](./ru-docs/Типы_псевдонимов.md)**
35 | * Создание собственных имён для сложных типов
36 | * Повышение читаемости и удобства работы с кодом
37 | * **[2.4 Типы Any, NoReturn:](./ru-docs/Типы_Any_и_NoReturn.md)**
38 | * Any: отключение проверки типов для конкретной переменной
39 | * NoReturn: указание на то, что функция не возвращает значение
40 | * **[2.5 Типы протоколов:](./ru-docs/Типы_протоколов.md)**
41 | * Определение интерфейсов для классов без явного наследования
42 | * Structural typing: проверка типов по структуре, а не по имени
43 | ## Модуль 3: Типизация в реальных проектах
44 | * **[3.1 Интеграция mypy в рабочий процесс:](./ru-docs/Интеграция_mypy.md)**
45 | * Настройка mypy для проверки типов в проекте
46 | * Интерпретация ошибок mypy и их исправление
47 | * **[3.2 Stub файлы:](./ru-docs/Stub_файлы.md)**
48 | * Что это такое и зачем нужны?
49 | * Как типизировать сторонние модули
50 | * Генерация stub файлов
51 |
52 | > [!IMPORTANT]
53 | > Если вам понравился этот курс, и вы подчерпнули из него что-то новое, пожалуйста, поддержите этот репозиторий звёздочкой.
54 | > Мне будет очень приятно :)
--------------------------------------------------------------------------------
/eng-docs/assets/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DIMFLIX/PyTyping-Course/861ff0845270ae43d2cfad8c70b56b59ba035bf8/eng-docs/assets/back.png
--------------------------------------------------------------------------------
/eng-docs/assets/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DIMFLIX/PyTyping-Course/861ff0845270ae43d2cfad8c70b56b59ba035bf8/eng-docs/assets/main.png
--------------------------------------------------------------------------------
/eng-docs/assets/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DIMFLIX/PyTyping-Course/861ff0845270ae43d2cfad8c70b56b59ba035bf8/eng-docs/assets/next.png
--------------------------------------------------------------------------------
/ru-docs/Stub_файлы.md:
--------------------------------------------------------------------------------
1 | # 3.2 Типизация и сторонние библиотеки
2 | Работа с типами в Python становится особенно важной, когда вы используете сторонние библиотеки. В этом разделе мы рассмотрим, как эффективно интегрировать типизацию в проекты, использующие внешние зависимости, а также как создавать стаб-файлы для библиотек, которые не предоставляют аннотации типов.
3 |
4 | ## Что такое стаб-файлы?
5 | Стаб-файлы (или stub files) — это специальные файлы с расширением `.pyi`, которые используются в **Python** для предоставления информации о типах данных в коде, особенно когда речь идет о внешних библиотеках, которые не содержат аннотаций типов. Они позволяют разработчикам использовать преимущества статической типизации, не изменяя исходный код библиотек.
6 |
7 | ## Зачем нужны?
8 | В Python, как и в других языках программирования, типизация помогает избежать ошибок, связанных с неправильным использованием данных. Например, если функция ожидает получить число, а вы передаете строку, это может привести к ошибкам во время выполнения программы. Стаб-файлы помогают:
9 | - **Указать ожидаемые типы аргументов и возвращаемых значений**: Это позволяет статическим анализаторам кода и IDE (например, PyCharm или VSCode) проверять ваш код на наличие ошибок до его выполнения.
10 | - **Упростить работу с нетипизированными библиотеками**: Если вы используете стороннюю библиотеку, которая не поддерживает аннотации типов, вы можете создать стаб-файл для этой библиотеки и указать в нем необходимые типы.
11 |
12 | ## Как работают?
13 | Стаб-файлы содержат описание функций и классов, которые предоставляет библиотека, но без их реализации. Вместо этого они используют многоточие (`...`), чтобы указать, что реализация находится в другом месте. Например:
14 |
15 | ```python
16 | # my_library.pyi
17 | def my_function(param: int) -> str: ...
18 | ```
19 |
20 | В этом примере мы указываем, что функция `my_function` принимает один аргумент типа `int` и возвращает значение типа `str`.
21 |
22 | ## Как создать стаб-файл?
23 | 1. **Определите функции и классы**: Изучите исходный код модуля, чтобы понять, какие функции и классы он предоставляет.
24 | 2. **Создайте файл с расширением `.pyi`**: Например, если ваш модуль называется `my_library`, создайте файл `my_library.pyi`.
25 | 3. **Опишите типы**: Внутри файла укажите типы для всех функций и классов.
26 |
27 | Пример стаб-файла для простой библиотеки:
28 | ```python
29 | # my_library.pyi
30 | def add(a: int, b: int) -> int: ...
31 |
32 | def greet(name: str) -> str: ...
33 | ```
34 |
35 | Если у вас есть сложная структура, организуйте стаб-файлы в подкаталоги с использованием `__init__.pyi` для пакетов.
36 |
37 | ## Где разместить стаб-файлы?
38 | Стаб-файлы могут быть размещены в разных местах:
39 | - **В той же директории, что и библиотека**: Это самый простой способ.
40 | - **В отдельной папке**: Вы можете создать папку для всех ваших стаб-файлов и добавить её в переменную окружения `MYPYPATH`, чтобы Python мог их найти.
41 |
42 | Пример добавления пути к стаб-файлам:
43 | ```bash
44 | export MYPYPATH=~/path/to/stubs
45 | ```
46 |
47 | ## Генерация стаб-файлов
48 | - **Использование `stubgen`**: Вы можете использовать инструмент `stubgen`, который входит в пакет `mypy`, чтобы автоматически генерировать стаб-файлы для сторонних библиотек:
49 | ```bash
50 | pip install -U mypy stubgen -m library_name
51 | ```
52 | - Сгенерированные файлы будут сохранены в директории `out/`. Однако имейте в виду, что автоматически сгенерированные файлы могут не содержать полной информации о типах и документации.
53 | - **Использование сторонних проектов**: Существуют проекты, которые предоставляют готовые стаб-файлы для популярных библиотек. Вы можете скачать их и разместить в директории с установленным модулем.
54 |
55 |
56 | ***
57 |
58 |
--------------------------------------------------------------------------------
/ru-docs/assets/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DIMFLIX/PyTyping-Course/861ff0845270ae43d2cfad8c70b56b59ba035bf8/ru-docs/assets/back.png
--------------------------------------------------------------------------------
/ru-docs/assets/main.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DIMFLIX/PyTyping-Course/861ff0845270ae43d2cfad8c70b56b59ba035bf8/ru-docs/assets/main.png
--------------------------------------------------------------------------------
/ru-docs/assets/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DIMFLIX/PyTyping-Course/861ff0845270ae43d2cfad8c70b56b59ba035bf8/ru-docs/assets/next.png
--------------------------------------------------------------------------------
/ru-docs/Базовые_типы_данных.md:
--------------------------------------------------------------------------------
1 | # 1.2 Базовые типы данных
2 | Python предлагает следующие базовые типы данных:
3 | * `int`: Целые числа (например: 10, -5, 0).
4 | * `float`: Числа с плавающей точкой (например: 3.14, -2.5, 0.0).
5 | * `str`: Строки (например: "hello", "world", "123").
6 | * `bool`: Логический тип, принимающий значения `True` или `False`.
7 |
8 | Примеры аннотации типов для базовых типов:
9 | ```python
10 | age: int = 30
11 | price: float = 19.99
12 | name: str = "Alice"
13 | is_active: bool = True
14 | ```
15 |
16 | ## None: отсутствие значения и его особенности
17 | `None` — специальный тип, который обозначает отсутствие значения.
18 | ```python
19 | result: None = None
20 | ```
21 |
22 | Важно понимать, что `None` — не то же самое, что 0, пустая строка или `False`. Это отдельный тип, который указывает на отсутствие какого-либо значения.
23 |
24 | ## Duck typing: неявная типизация в Python
25 | Python также использует концепцию duck typing. Это неявная типизация, основанная на том, что "если что-то ходит как утка и крякает как утка, то это, вероятно, утка".
26 |
27 | Другими словами, Python не всегда строго проверяет типы объектов, а смотрит на то, какие методы и атрибуты у них есть. Если объект поддерживает необходимые методы и атрибуты, то он считается подходящим, независимо от его конкретного типа.
28 |
29 | ```python
30 | def print_length(obj):
31 | print(len(obj))
32 |
33 | print_length("hello") # Вывод: 5
34 | print_length([1, 2, 3]) # Вывод: 3
35 | ```
36 |
37 | В этом примере функция `print_length` принимает любой объект, у которого есть метод `len`. Она не проверяет конкретный тип объекта, что является примером duck typing.
38 |
39 | **Duck typing** делает Python гибким, но может усложнить отладку кода, так как ошибки типов могут проявиться только во время выполнения. Использование аннотаций типов и статических анализаторов помогает снизить риски, связанные с duck typing, и повысить надежность кода.
40 |
41 |
42 | ***
43 |
44 |
--------------------------------------------------------------------------------
/ru-docs/Введение_в_типизацию.md:
--------------------------------------------------------------------------------
1 | # 1.1 Введение в типизацию
2 | В этом разделе мы рассмотрим, почему типизация важна в Python, когда ее стоит применять и как она реализована в языке.
3 |
4 | ## Динамическая и статическая типизация: плюсы и минусы
5 | Python — язык с динамической типизацией. Это значит, что тип переменной определяется во время выполнения программы, а не во время написания кода. Такой подход обладает рядом **преимуществ**:
6 | * Гибкость: Вы можете легко менять типы переменных на лету, не беспокоясь о строгих ограничениях компилятора.
7 | * Быстрое прототипирование: Динамическая типизация позволяет быстро создавать прототипы, фокусируясь на логике программы, а не на формальностях типов.
8 | * Меньше кода: Вам не нужно объявлять типы переменных, что делает код более лаконичным.
9 |
10 | Однако у динамической типизации есть и **недостатки**:
11 | * Ошибки типов во время выполнения: Ошибки, связанные с неверным использованием типов, могут проявиться только во время работы программы, что усложняет отладку.
12 | * Снижение читаемости кода: Без явного указания типов сложнее понять, какие данные хранятся в переменных и как они используются.
13 | * Проблемы с масштабированием: В больших проектах с множеством разработчиков отсутствие строгой типизации может привести к путанице и ошибкам, которые сложно отследить.
14 |
15 | Статическая типизация решает эти проблемы. Она требует, чтобы типы переменных были объявлены заранее, во время написания кода. Это позволяет:
16 | * Обнаруживать ошибки типов на этапе компиляции: Компилятор или статический анализатор кода проверяет типы, предотвращая ошибки во время выполнения программы.
17 | * Повысить читаемость кода: Явное указание типов делает код более понятным и легким для восприятия.
18 | * Упростить рефакторинг: Статическая типизация помогает легче и безопаснее изменять код, так как компилятор или анализатор кода может обнаружить несоответствия типов.
19 |
20 | ## Типизация в Python: история и эволюция
21 | Python изначально был языком с динамической типизацией. Однако с ростом популярности языка и увеличением размеров проектов разработчики начали испытывать необходимость в статической типизации.
22 |
23 | В **Python 3.5** были введены аннотации типов: возможность добавлять информацию о типах к переменным, аргументам функций и возвращаемым значениям.
24 |
25 | > [!WARNING]
26 | > Они никак не влияют на работу программы, но могут использоваться статическими анализаторами кода, например **mypy**.
27 |
28 | В Python 3.6 появился новый синтаксис для переменных с объявленными типами. Это позволяет объявлять тип переменной, используя двоеточие и имя типа: `age: int = 25`.
29 |
30 | ## Инструменты для статической типизации: mypy, PyCharm, другие
31 | Для работы со статической типизацией в Python существует ряд инструментов:
32 | * **mypy**: Самый популярный статический анализатор кода для Python. Он проверяет типы в коде и сообщает об ошибках.
33 | * **PyCharm**: Популярная IDE для Python, которая включает встроенную поддержку статической типизации и интеграцию с mypy.
34 | * Другие инструменты: Существуют и другие инструменты для статической типизации, например Pyre, Pytype, которые предлагают свои особенности и возможности.
35 |
36 | В этом курсе мы будем использовать mypy в качестве основного инструмента для статической типизации, так как он является наиболее распространенным и хорошо поддерживаемым инструментом.
37 |
38 | ***
39 |
40 |
41 |
--------------------------------------------------------------------------------
/ru-docs/Интеграция_mypy.md:
--------------------------------------------------------------------------------
1 | # 3.1 Интеграция mypy
2 | Многие разработчики Python сталкиваются с необходимостью улучшения качества кода и повышения его устойчивости к ошибкам. Одним из эффективных способов достижения этих целей является **статическая типизация**. В данном контексте инструмент mypy становится незаменимым помощником. В этом разделе мы рассмотрим, как интегрировать mypy в ваш рабочий процесс, чтобы обеспечить надежность и понятность вашего кода.
3 |
4 | ## Настройка mypy для проверки типов в проекте
5 | Чтобы начать работу с mypy, первое, что нужно сделать, - это добавить его в ваш проект. Вы можете установить mypy с помощью пакетного менеджера pip:
6 |
7 | ```bash
8 | pip install mypy
9 | ```
10 |
11 | После установки рекомендуется создать файл конфигурации **mypy**.
12 | Хорошей практикой является создание файла под следующими названиями:
13 | - `mypy.ini`
14 | - `.mypy.ini`
15 | - `pyproject.toml`
16 | - `setup.cfg`
17 |
18 | Вот пример файла конфигурации:
19 |
20 | ```toml
21 | [mypy]
22 | files = src/**/*.py # Путь к исходным файлам, которые будут проверяться mypy
23 | ignore_missing_imports = True # Игнорировать ошибки отсутствия импортов
24 | strict = True # Включить строгий режим проверки
25 |
26 | disallow_untyped_defs=True # запрещает объявлять функции без аннотации типов
27 | no_implicit_optional=True # без этой опции аргументы функции могут иметь тип None в качестве параметра по-умолчанию. С этой опцией такое неявное поведение запрещено, а гарантии типов сохранены
28 | warn_return_any=True # С этой опцией mypy предупреждает, если в качестве возвращаемого типа было указано Any
29 |
30 | check_untyped_defs=True # проверяет корректность типизации в функции без аннотаций. По-умолчанию mypy закрывает глаза на все, что происходит внутри таких функций. С этой опцией он проверяет корректность типизации всех операций и вызовов внутри;
31 | warn_unreachable=True # сообщает, если какой-то кусок кода никогда не выполнается. Если так случилось, вероятно была допущена ошибка в проверке условий.
32 | ```
33 |
34 |
35 | ## Интерпретация ошибок mypy и их исправление
36 | После настройки mypy вы можете запускать его через командную строку, просто введя команду:
37 |
38 | ```bash
39 | mypy .
40 | ```
41 |
42 | Эта команда начнет проверку типов во всех ваших файлах Python и выведет результаты на экран. Если mypy обнаружит несоответствия, он предоставит полезную информацию о том, где именно в коде произошла ошибка. Вы увидите сообщение об ошибке, а также строку и номер файла, где эта ошибка возникла.
43 |
44 | Ошибки могут быть очень разнообразными: от несоответствия ожидаемого и фактического типов до отсутствия аннотаций для функций и классов. Разработчики должны внимательно рассмотреть каждую ошибку и внести соответствующие изменения в код. Например, как только вы добавите аннотации типов в свои функции, mypy начнет распознавать, что ожидалось, и указывать на те случаи, когда фактический тип не совпадает.
45 |
46 | ### Пример некорректной аннотации типов
47 |
48 | ```python
49 | def add_numbers(a: int, b: str) -> int:
50 | return a + b # Ошибка: попытка сложить int и str
51 | ```
52 |
53 | Запустив mypy, вы получите указание на ошибку, что позволит вам быстро посмотреть на функцию и внести необходимые изменения, чтобы гарантировать корректность работы.
54 |
55 | ***
56 |
57 |
--------------------------------------------------------------------------------
/ru-docs/Классы_и_типизация.md:
--------------------------------------------------------------------------------
1 | # 1.5 Классы и типизация
2 | Типизация играет важную роль в объектно-ориентированном программировании на Python. Она позволяет создавать более надежный и понятный код, работая с классами и объектами.
3 |
4 | ## Аннотация типов для атрибутов класса
5 | Вы можете использовать аннотации типов для определения типов атрибутов класса. Это делает код более читаемым и позволяет статическим анализаторам проверять корректность использования атрибутов.
6 | ```python
7 | class Dog:
8 | name: str
9 | age: int
10 |
11 | def __init__(self, name: str, age: int):
12 | self.name = name
13 | self.age = age
14 |
15 | def bark(self) -> str:
16 | return "Woof!"
17 |
18 | my_dog: Dog = Dog("Buddy", 3)
19 | print(f"{my_dog.name} is {my_dog.age} years old.") # Buddy is 3 years old.
20 | print(my_dog.bark()) # Woof!
21 | ```
22 |
23 | В этом примере мы указываем типы атрибутов `name` (str) и `age` (int) в теле класса.
24 |
25 | ## self и cls: типизация в методах класса
26 | В методах класса `self` ссылается на текущий экземпляр объекта, а `cls` - на сам класс.
27 | * `self`: Тип `self` обычно выводится автоматически и не требует явной аннотации.
28 | * `cls`: Для типизации `cls` используется `Type[T]`, где `T` - это тип класса.
29 |
30 | ```python
31 | from typing import Type
32 |
33 | class Animal:
34 | name: str
35 |
36 | def __init__(self, name: str):
37 | self.name = name
38 |
39 | @classmethod
40 | def create(cls: Type["Animal"], name: str) -> "Animal":
41 | return cls(name)
42 |
43 | my_animal: Animal = Animal.create("Leo")
44 | print(my_animal.name) # Output: Leo
45 | ```
46 |
47 | В этом примере `cls` в методе `create` имеет тип `Type["Animal"]`, что указывает на то, что он ссылается на сам класс `Animal`.
48 |
49 | ## Наследование и типизация
50 | При наследовании типов важно учитывать, что дочерние классы могут переопределять методы родительских классов. Аннотации типов должны быть совместимы с типами в родительском классе, чтобы избежать ошибок типизации.
51 | ```python
52 | class Animal:
53 | name: str
54 |
55 | def speak(self) -> str:
56 | raise NotImplementedError
57 |
58 | class Dog(Animal):
59 | def speak(self) -> str:
60 | return "Woof!"
61 |
62 | class Cat(Animal):
63 | def speak(self) -> str:
64 | return "Meow!"
65 | ```
66 |
67 | В этом примере метод `speak` в родительском классе `Animal` не имеет реализации (`NotImplementedError`). Дочерние классы `Dog` и `Cat` переопределяют этот метод, предоставляя свою реализацию. Важно, чтобы возвращаемый тип метода `speak` в дочерних классах был совместим с типом, указанным в родительском классе (`str`).
68 |
69 | Типизация классов и объектов в Python способствует созданию более структурированного, надежного и понятного кода, что особенно важно в крупных проектах.
70 |
71 |
72 | ***
73 |
74 |
--------------------------------------------------------------------------------
/ru-docs/Коллекции_и_их_типизация.md:
--------------------------------------------------------------------------------
1 | # 1.3 Коллекции и их типизация:
2 | Python предлагает мощные встроенные структуры данных - коллекции. В этом разделе мы рассмотрим, как типизировать коллекции для повышения надежности и читаемости кода.
3 |
4 | ## list, tuple, dict, set: работа с коллекциями
5 | Python предлагает следующие встроенные типы коллекций:
6 | * **`list`**: Упорядоченный изменяемый набор элементов.
7 | * Пример: `[1, 2, 3, "hello"]`
8 | * **`tuple`**: Упорядоченный неизменяемый набор элементов.
9 | * Пример: `(1, 2, "hello", True)`
10 | * **`dict`**: Набор пар "ключ-значение", где ключи должны быть уникальными и неизменяемыми.
11 | * Пример: `{"name": "Alice", "age": 30}`
12 | * **`set`**: Неупорядоченный набор уникальных элементов.
13 | * Пример: `{1, 2, 3, 3}` (дубликаты игнорируются)
14 |
15 | ## Типизация коллекций: List\[int\], Tuple\[str, int\], Dict\[str, float\]
16 | Для типизации коллекций используются **дженерики** (generics) - специальные типы, которые параметризуются другими типами. В Python дженерики представляются в виде квадратных скобок после имени типа.
17 |
18 | Примеры:
19 | * `List[int]`: список целых чисел.
20 | * `Tuple[str, int]`: кортеж, содержащий строку и целое число.
21 | * `Dict[str, float]`: словарь, где ключи - строки, а значения - числа с плавающей точкой.
22 | * `Set[bool]`: множество логических значений.
23 |
24 | numbers: List[int] = [1, 2, 3]
25 | person: Tuple[str, int] = ("Bob", 25)
26 | prices: Dict[str, float] = {"apple": 0.99, "banana": 1.25}
27 | active_users: Set[bool] = {True, False}
28 |
29 | ## Генераторы и итераторы: типизация и особенности
30 | **Генераторы** - это функции, которые используют оператор `yield` для возврата последовательности значений. **Итераторы** - это объекты, которые реализуют метод `__next__`, позволяющий получать следующий элемент последовательности.
31 |
32 | Типизация генераторов и итераторов использует специальные типы:
33 | * `Iterator[T]`: итератор, возвращающий значения типа T.
34 | * `Iterable[T]`: объект, по которому можно итерироваться, возвращая значения типа T.
35 | * `Generator[T, R, S]`: генератор, который возвращает значения типа T, принимает значения типа R и возвращает финальное значение типа S (необязательно).
36 |
37 | ```python
38 | from typing import Iterator, Iterable, Generator
39 |
40 | def even_numbers(n: int) -> Iterator[int]:
41 | for i in range(n):
42 | if i % 2 == 0:
43 | yield i
44 |
45 | numbers: Iterable[int] = even_numbers(10)
46 |
47 | def my_generator(start: int, end: int) -> Generator[int, None, int]:
48 | total = 0
49 | for i in range(start, end):
50 | total += i
51 | yield i
52 | return total
53 |
54 | result: int = my_generator(1, 5)
55 | ```
56 |
57 | > [!IMPORTANT]
58 | > При использовании `Generator` необязательно указывать все три типа.
59 | > Если генератор не принимает значений или не возвращает финальное значение, можно использовать `None`.
60 |
61 |
62 | ***
63 |
64 |
--------------------------------------------------------------------------------
/ru-docs/Типизация_с_использованием_TypeVar.md:
--------------------------------------------------------------------------------
1 | # 2.1 Типизация с использованием TypeVar
2 | TypeVar - это инструмент, предоставляемый модулем `typing` в `Python`, который позволяет создавать универсальные функции и классы, работающие с различными типами данных. Он дает возможность определять обобщенные типы, которые могут быть заменены конкретными типами во время выполнения программы.
3 |
4 | ## Создание универсальных функций и классов
5 | С помощью `TypeVar` вы можете создавать функции и классы, которые могут работать с различными типами данных. Это достигается за счет использования переменных типа, которые могут быть заменены на конкретные типы при вызове функции или создании экземпляра класса.
6 |
7 | Например, рассмотрим функцию, которая принимает два аргумента одного и того же типа и возвращает их сумму:
8 | ```python
9 | from typing import TypeVar
10 |
11 | T = TypeVar('T', int, float)
12 |
13 | def add(a: T, b: T) -> T:
14 | return a + b
15 | ```
16 |
17 | В этом примере `T` - это переменная типа, определенная с помощью `TypeVar`. Она может быть заменена на int или float при вызове функции `add()`. Таким образом, функция может работать как с целыми, так и с плавающими числами.
18 |
19 | ## Ограничение типов с помощью TypeVar
20 | Вы также можете ограничить типы, которые могут быть использованы с `TypeVar`, используя дополнительные аргументы. Это позволяет вам создавать более специализированные универсальные функции и классы.
21 |
22 | Например, вы можете ограничить `TypeVar` до типов, которые поддерживают операцию сложения:
23 | ```python
24 | from typing import TypeVar, Addable
25 |
26 | AddableT = TypeVar('AddableT', bound=Addable)
27 |
28 | def add(a: AddableT, b: AddableT) -> AddableT:
29 | return a + b
30 | ```
31 |
32 | В этом примере `AddableT` - это `TypeVar`, ограниченный типами, которые поддерживают операцию сложения (т.е. реализуют протокол `Addable`). Это гарантирует, что функция `add` может работать только с типами, для которых определена операция сложения.
33 |
34 | ## Практические примеры использования TypeVar
35 | Вот несколько практических примеров использования `TypeVar`:
36 | ### 1. Универсальный контейнер:
37 | ```python
38 | from typing import TypeVar, Generic
39 |
40 | T = TypeVar('T')
41 |
42 | class Container(Generic[T]):
43 | def __init__(self, value: T):
44 | self.value = value
45 |
46 | def get(self) -> T:
47 | return self.value
48 | ```
49 |
50 | Этот класс `Container` может хранить и возвращать значения любого типа, определяемого с помощью `T`.
51 | ### 2. Универсальный сортировщик:
52 | ```python
53 | from typing import TypeVar, Callable, List
54 |
55 | T = TypeVar('T')
56 |
57 | def sort(items: List[T], key: Callable[[T], Any]) -> List[T]:
58 | return sorted(items, key=key)
59 | ```
60 |
61 | Эта функция `sort` может сортировать списки любых типов, используя пользовательскую функцию `key`.
62 | ### 3. Универсальный репрезентативный метод:
63 | ```python
64 | from typing import TypeVar, Generic
65 |
66 | T = TypeVar('T')
67 |
68 | class Person(Generic[T]):
69 | def __init__(self, name: str, data: T):
70 | self.name = name
71 | self.data = data
72 |
73 | def __repr__(self) -> str:
74 | return f"Person(name='{self.name}', data={self.data!r})"
75 | ```
76 |
77 | В этом примере класс Person использует `TypeVar T`, чтобы позволить хранить данные любого типа в атрибуте data. Метод `__repr__` также использует `T`, чтобы обеспечить универсальное строковое представление объекта.
78 |
79 | ### 4. Универсальный генератор:
80 | ```python
81 | from typing import TypeVar, Iterable, Callable
82 |
83 | T = TypeVar('T')
84 | U = TypeVar('U')
85 |
86 | def map_generator(iterable: Iterable[T], func: Callable[[T], U]) -> Iterable[U]:
87 | for item in iterable:
88 | yield func(item)
89 | ```
90 |
91 | Функция `map_generator` использует два `TypeVar` (`T` и `U`), чтобы создать универсальный генератор, который применяет заданную функцию к каждому элементу итерируемого объекта.
92 |
93 | ***
94 |
95 |
--------------------------------------------------------------------------------
/ru-docs/Типы_Any_и_NoReturn.md:
--------------------------------------------------------------------------------
1 | # 2.4 Типы Any, NoReturn
2 | В Python, помимо стандартных типов данных, существуют специальные типы, которые могут быть полезны в определенных ситуациях. Два таких типа - **Any** и **NoReturn**.
3 |
4 | ## Any: отключение проверки типов для конкретной переменной
5 | Тип **Any** позволяет отключить проверку типов для конкретной переменной. Это может быть полезно, когда вы работаете с динамическими или неизвестными типами данных, или когда вы хотите временно отключить проверку типов для отладки или экспериментов.
6 | ```python
7 | from typing import Any
8 |
9 | def process_data(data: Any) -> None:
10 | print(f"Processing data: {data}")
11 | # Здесь вы можете работать с data, не заботясь о его типе
12 |
13 | process_data(42)
14 | process_data("hello")
15 | process_data([1, 2, 3])
16 | ```
17 |
18 | В этом примере функция `process_data()` принимает аргумент типа **Any**, что означает, что она может принимать значения любого типа. Это может быть полезно, когда вы не знаете заранее, какой тип данных будет передан в функцию.
19 |
20 | > [!WARNING]
21 | > Использование **Any** следует применять **осторожно**, так как оно отключает проверку типов и может привести к ошибкам во время выполнения. Старайтесь использовать **Any** только в тех случаях, когда это действительно необходимо.
22 |
23 | ## NoReturn: указание на то, что функция не возвращает значение
24 | Тип **NoReturn** используется для обозначения того, что функция не возвращает никакого значения. Это может быть полезно, когда вы пишете функции, которые выполняют некоторые действия, но не возвращают результат, например, функции, которые выводят сообщения или выполняют побочные эффекты.
25 | ```python
26 | from typing import NoReturn
27 |
28 | def print_message(message: str) -> NoReturn:
29 | print(f"Message: {message}")
30 | # Функция не возвращает никакого значения
31 |
32 | print_message("Hello, world!")
33 | ```
34 |
35 | В этом примере функция `print_message()` объявлена как возвращающая **NoReturn**, что означает, что она не возвращает никакого значения. Это помогает сделать ваш код более понятным и предотвратить ошибки, связанные с неожиданным возвращаемым значением.
36 |
37 | > [!IMPORTANT]
38 | > Использование NoReturn помогает сделать ваш код более самодокументируемым и предотвратить ошибки, связанные с неожиданным возвращаемым значением.
39 |
40 | ***
41 |
42 |
--------------------------------------------------------------------------------
/ru-docs/Типы_объединений_и_опциональные_типы.md:
--------------------------------------------------------------------------------
1 | # 2.2 Типы объединений и опциональные типы
2 | В Python, когда мы работаем с переменными, часто возникает необходимость иметь дело с данными, которые могут иметь разные типы. Для этого нам на помощь приходят типы объединений (Union) и опциональные типы (Optional).
3 |
4 | ## Работа с переменными, которые могут иметь разные типы
5 | Тип объединения `Union` позволяет указать, что переменная может быть одного из нескольких типов. Это достигается с помощью импорта `Union` из модуля typing и определения типа переменной следующим образом:
6 | ```python
7 | from typing import Union
8 |
9 | def process_value(value: Union[int, str]) -> None:
10 | print(f"The value is: {value}")
11 |
12 | process_value(42) # The value is: 42
13 | process_value("hello") # The value is: hello
14 | ```
15 |
16 | В этом примере функция `process_value()` может принимать значения типа **int** или **str**. Это позволяет нам вызывать эту функцию с аргументами разных типов.
17 |
18 | > [!info]
19 | > Использование типов объединений помогает сделать ваш код более гибким и универсальным, позволяя работать с данными различных типов.
20 |
21 | ## Использование Optional для обозначения возможности отсутствия значения
22 | Иногда нам нужно работать с переменными, которые могут иметь значение или быть None. Для этого мы можем использовать тип `Optional`, который является **синтаксическим сахаром** для `Union[Some_Type, None]`.
23 | ```python
24 | from typing import Optional
25 |
26 | def get_length(text: Optional[str]) -> int:
27 | if text is None:
28 | return 0
29 | else:
30 | return len(text)
31 |
32 | print(get_length("hello")) # Output: 5
33 | print(get_length(None)) # Output: 0
34 | ```
35 |
36 | В этом примере функция `get_length()` принимает аргумент **text** типа `Optional[str]`. Это означает, что функция может быть вызвана как с значением типа **str**, так и с значением типа **None**. Внутри функции мы проверяем, является ли **text** - **None**, и возвращаем соответствующее значение.
37 |
38 | > [!IMPORTANT]
39 | > Использование Optional помогает явно указать, что переменная может быть None, что делает ваш код более понятным и предотвращает ошибки, связанные с неожиданным отсутствием значения.
40 |
41 | Комбинируя типы объединений и опциональные типы, вы можете создавать еще более гибкие и выразительные типы данных, которые лучше отражают структуру ваших данных и помогают предотвращать ошибки на этапе разработки.
42 |
43 | ***
44 |
45 |
--------------------------------------------------------------------------------
/ru-docs/Типы_протоколов.md:
--------------------------------------------------------------------------------
1 | # 2.5 Типы протоколов
2 | В Python, помимо стандартных типов данных, существует еще один интересный инструмент для работы с типами - типы протоколов. Они используют **structural typing**, что означает, что проверка типов происходит по структуре объекта, а не по его имени. Это означает, что объект будет считаться соответствующим протоколу, если он имеет все необходимые методы и атрибуты, даже если он не наследует от протокола явно.
3 |
4 | ## Определение интерфейсов для классов без явного наследования
5 | Типы протоколов позволяют определять интерфейсы для классов, не требуя явного наследования. Это может быть полезно, когда вы работаете с классами, которые не находятся под вашим контролем, или когда вы хотите создать более гибкую систему типизации.
6 |
7 | Рассмотрим пример:
8 | ```python
9 | from typing import Protocol
10 |
11 | class Printable(Protocol):
12 | def print_something(self, message: str) -> None:
13 | ...
14 |
15 | def process_printable(obj: Printable) -> None:
16 | obj.print_something("Hello, world!")
17 |
18 | class MyClass:
19 | def print_something(self, message: str) -> None:
20 | print(f"MyClass says: {message}")
21 |
22 | my_object = MyClass()
23 | process_printable(my_object)
24 | ```
25 |
26 | В этом примере мы определяем протокол `Printable`, который требует, чтобы объекты, которые его реализуют, имели метод `print_something()`. Затем мы определяем функцию `process_printable()`, которая принимает объект, реализующий протокол `Printable`, и вызывает его метод `print_something()`.
27 |
28 | Обратите внимание, что `MyClass` не наследует от Printable, но все равно может быть использована в `process_printable()`, потому что она реализует необходимый интерфейс.
29 |
30 | > [!IMPORTANT]
31 | > Типы протоколов позволяют определять интерфейсы для классов без явного наследования, что делает ваш код более гибким и расширяемым.
32 |
33 |
34 | ***
35 |
36 |
--------------------------------------------------------------------------------
/ru-docs/Типы_псевдонимов.md:
--------------------------------------------------------------------------------
1 | # 2.3 Типы псевдонимов
2 | В Python, когда мы работаем с более сложными типами данных, такими как словари, списки или кортежи, нам может понадобиться создавать собственные имена для этих типов, чтобы сделать наш код более читаемым и удобным в работе.
3 |
4 | ## Создание собственных имён для сложных типов
5 | Для этого мы можем использовать функцию `TypeAlias` из модуля `typing`. Она позволяет нам создавать псевдонимы для существующих типов данных.
6 |
7 | Например, представим, что мы часто работаем с данными, представленными в виде словаря, где ключи - строки, а значения - целые числа:
8 | ```python
9 | from typing import TypeAlias
10 |
11 | # Создаем псевдоним для типа словаря
12 | NumberDict: TypeAlias = dict[str, int]
13 |
14 | def process_numbers(data: NumberDict) -> None:
15 | for key, value in data.items():
16 | print(f"Key: {key}, Value: {value}")
17 |
18 | example_data: NumberDict = {
19 | "apple": 5,
20 | "banana": 3,
21 | "cherry": 10
22 | }
23 |
24 | process_numbers(example_data)
25 | ```
26 |
27 | В этом примере мы создаем псевдоним **NumberDict** для типа `dict[str, int]`. Это делает наш код более читаемым и понятным, так как мы явно указываем, что ожидаем словарь, где ключи - строки, а значения - целые числа.
28 |
29 | > [!info]
30 | > Использование типов псевдонимов помогает сделать ваш код более выразительным и понятным для других разработчиков, которые будут работать с вашим проектом.
31 |
32 | ## Повышение читаемости и удобства работы с кодом
33 | Представим ситуацию, когда у нас присутствует объект с множеством вложенностей, которые нам нужно типизировать
34 | ```python
35 | from typing import TypeAlias
36 |
37 | Products: TypeAlias = dict[str, dict[str, list[str]]]
38 |
39 | def set_shop_products(data: Products) -> None:
40 | ...
41 |
42 | # Пример структуры онлайн магазина
43 | # {
44 | # категория: {
45 | # продукты: [ключи/аккануты]
46 | # },
47 | # }
48 | example_data: Products = {
49 | "Игры": {
50 | "Don't Starve Together": [
51 | "key1",
52 | "key2",
53 | "key3"
54 | ],
55 | "Forza Horizon 5": [
56 | "key1",
57 | "key2",
58 | "key3"
59 | ]
60 | },
61 | "Кино": {
62 | "Кинопоиск": [
63 | "account1",
64 | "account2",
65 | "account3"
66 | ],
67 | "Иви": [
68 | "account1",
69 | "account2",
70 | "account3"
71 | ]
72 | }
73 | }
74 | process_person(example_data)
75 | ```
76 |
77 | Здесь мы реализовали огромный объект, имеющий несколько вложенностей. Нам нужно его типизировать. Представляете, что-бы было, если бы нам пришлось аннотировать несколько функций которые принимали и возвращали объект такого типа. Было бы что-то на подобии такого:
78 |
79 | ```python
80 | def process_shop_data(data: dict[str, dict[str, list[str]]]) -> dict[str, dict[str, list[str]]]:
81 | ...
82 |
83 | example_data: dict[str, dict[str, list[str]]] = ...
84 | processed_shop_data: dict[str, dict[str, list[str]]] = process_shop_data(example_data)
85 | ```
86 |
87 | Слишком много кода, не так-ли?
88 | Именно в таких случаях и стоит применять типы псевдонимов.
89 |
90 | ***
91 |
92 |
--------------------------------------------------------------------------------
/ru-docs/Функции_и_типизация.md:
--------------------------------------------------------------------------------
1 | # 1.4 Функции и типизация
2 | Функции являются основными строительными блоками любой программы. В Python, типизация функций играет важную роль в обеспечении надежности, читаемости и поддерживаемости кода.
3 |
4 | ## Аннотация типов для аргументов и возвращаемого значения
5 | Аннотации типов позволяют явно указывать типы аргументов и возвращаемого значения функции.
6 |
7 | ```python
8 | def greet(name: str) -> str:
9 | """Возвращает приветствие для заданного имени."""
10 | return "Привет, " + name + "!"
11 |
12 | message: str = greet("Alice")
13 | print(message) # Вывод: Привет, Alice!
14 | ```
15 |
16 | В этом примере мы указываем, что функция `greet` принимает аргумент `name` типа `str` и возвращает значение типа `str`. Аннотация типа следует за именем аргумента, отделенная двоеточием, и после стрелки `->`, указывающей тип возвращаемого значения.
17 |
18 | ## Перегрузка функций и типы
19 | В Python нет прямой поддержки перегрузки функций, как в некоторых других языках. Однако, мы можем использовать опциональные аргументы и аннотации типов, чтобы имитировать подобное поведение.
20 |
21 | ```python
22 | from typing import Union
23 |
24 | def add(a: Union[int, float], b: Union[int, float] = 0) -> Union[int, float]:
25 | """Складывает два числа, при этом второе число может быть опциональным."""
26 | return a + b
27 |
28 | result1: int = add(5, 3) # 8
29 | result2: float = add(2.5, 1.5) # 4.0
30 | result3: int = add(10) # 10
31 | ```
32 |
33 | В этом примере функция `add` может принимать как целые числа, так и числа с плавающей точкой. Тип `Union` позволяет указать, что аргумент может принимать значения одного из нескольких типов.
34 |
35 | ## Callable: работа с функциональными объектами
36 | `Callable` — это специальный тип, представляющий любой вызываемый объект, например, функцию.
37 |
38 | ```python
39 | from typing import Callable
40 |
41 | def apply(func: Callable[[int], str], value: int) -> str:
42 | """Применяет функцию к заданному значению."""
43 | return func(value)
44 |
45 | def int_to_str(x: int) -> str:
46 | return str(x)
47 |
48 | result: str = apply(int_to_str, 10)
49 | print(result) # Вывод: "10"
50 | ```
51 |
52 | В этом примере `func` — это аргумент типа `Callable[[int], str]`, что означает, что это функция, которая принимает один аргумент типа `int` и возвращает значение типа `str`.
53 |
54 | Использование аннотаций типов для функций значительно повышает читаемость и надежность кода, позволяя статическим анализаторам, таким как mypy, выявлять потенциальные ошибки на этапе компиляции.
55 |
56 | ***
57 |
58 |
--------------------------------------------------------------------------------