
Все примеры, приведённые в этом материале, написаны с использованием Python 3.7. В описании каждой возможности имеются сведения о минимальной версии Python, необходимой для её применения.
Форматные строки (3.6+)
Без использования строк сложно написать что-то полезное на любом языке программирования. Но для эффективной работы со строками разработчику нужны удобные инструменты. Такие инструменты, которые позволяют оперировать сложными структурами, не теряя при этом душевного равновесия. Большинство Python-разработчиков пользуется методом
format
:user = "Jane Doe"
action = "buy"
log_message = 'User {} has logged in and did an action {}.'.format(
user,
action
)
print(log_message)
# User Jane Doe has logged in and did an action buy.
Python 3, наряду с методом
format
, поддерживает форматные строки (f-strings, f-строки). Они представляют собой гибкий инструмент для выполнения различных манипуляций со строками. Вот как выглядит предыдущий пример, переписанный с использованием форматных строк:user = "Jane Doe"
action = "buy"
log_message = f'User {user} has logged in and did an action {action}.'
print(log_message)
# User Jane Doe has logged in and did an action buy.
Модуль pathlib (3.4+)
Форматные строки — замечательная технология, но для работы с некоторыми строками, вроде путей к файлам, созданы специальные инструменты, сильно упрощающие манипуляции с ними. В Python 3 имеется модуль pathlib, который представляет собой удобную абстракцию для работы с путями к файлам. Если вы пока не уверены в полезности этого модуля для решения ваших задач — взгляните на этот материал.
from pathlib import Path
root = Path('post_sub_folder')
print(root)
# post_sub_folder
path = root / 'happy_user'
# Делаем путь абсолютным
print(path.resolve())
# /home/weenkus/Workspace/Projects/DataWhatNow-Codes/how_your_python3_should_look_like/post_sub_folder/happy_user
Аннотации типов (3.5+)
Что лучше — статическая или динамическая типизация? Пожалуй, почти каждый программист имеет собственный ответ на этот непростой вопрос. Я оставляю на усмотрение читателей то, как именно они типизируют свои программы. Но я считаю, что всем полезно будет хотя бы знать о том, что Python 3 поддерживает аннотации типов.
def sentence_has_animal(sentence: str) -> bool:
return "animal" in sentence
sentence_has_animal("Donald had a farm without animals")
# True
Перечисления (3.4+)
Python 3 поддерживает, благодаря классу
Enum
, простой механизм работы с перечислениями. Перечисления удобно использовать для хранения списков констант. Константы, в противном случае, оказываются беспорядочно разбросанными в коде.from enum import Enum, auto
class Monster(Enum):
ZOMBIE = auto()
WARRIOR = auto()
BEAR = auto()
print(Monster.ZOMBIE)
# Monster.ZOMBIE
Из документации по Python 3 можно узнать о том, что перечисление — это набор символических имён (членов), привязанных к уникальным, неизменным значениям. Члены одного перечисления можно сравнивать на идентичность. Перечисления можно обходить.
for monster in Monster:
print(monster)
# Monster.ZOMBIE
# Monster.WARRIOR
# Monster.BEAR
Встроенный LRU-кэш (3.2+)
В наши дни механизмы кэширования применяются практически во всех программных и аппаратных системах. Python 3 значительно упрощает кэширование благодаря декоратору lru_cache, который реализует алгоритм LRU-кэширования (Least Recently Used).
Ниже показана функция, которая вычисляет числа Фибоначчи. Эта функция вынуждена по много раз выполнять одни и те же операции в ходе рекурсивных вызовов. В результате оказывается, что её производительность можно улучшить благодаря кэшированию.
import time
def fib(number: int) -> int:
if number == 0: return 0
if number == 1: return 1
return fib(number-1) + fib(number-2)
start = time.time()
fib(40)
print(f'Duration: {time.time() - start}s')
# Duration: 30.684099674224854s
Теперь используем
lru_cache
для оптимизации этой функции (такая техника оптимизации называется мемоизацией). В результате время выполнения функции, которое раньше измерялось секундами, теперь измеряется наносекундами.from functools import lru_cache
@lru_cache(maxsize=512)
def fib_memoization(number: int) -> int:
if number == 0: return 0
if number == 1: return 1
return fib_memoization(number-1) + fib_memoization(number-2)
start = time.time()
fib_memoization(40)
print(f'Duration: {time.time() - start}s')
# Duration: 6.866455078125e-05s
Распаковка итерируемых объектов (3.0+)
При распаковке итерируемых объектов можно использовать переменные, перед именами которых ставят звёздочку. В такие переменные попадает всё то, что не попало в другие переменные. Так, в следующем примере в переменные
head
и tail
попадают первое и последнее значения из списка, сформированного командой range(5)
. В переменную body
попадает всё то, что находится между первым и последним значением.head, *body, tail = range(5)
print(head, body, tail)
# 0 [1, 2, 3] 4
py, filename, *cmds = "python3.7 script.py -n 5 -l 15".split()
print(py)
print(filename)
print(cmds)
# python3.7
# script.py
# ['-n', '5', '-l', '15']
first, _, third, *_ = range(10)
print(first, third)
# 0 2
Классы данных (3.7+)
В Python 3 появились классы данных (data classes). Они дают программисту достаточно большую свободу действий. Их можно использовать для уменьшения объёма шаблонного кода. Дело в том, что декоратор
dataclass
автоматически генерирует специальные методы, такие как __init__()
и __repr__()
. В официальном тексте соответствующего предложения они описаны как «изменяемые именованные кортежи со значениями по умолчанию». Вот пример создания класса без использования декоратора dataclass
:class Armor:
def __init__(self, armor: float, description: str, level: int = 1):
self.armor = armor
self.level = level
self.description = description
def power(self) -> float:
return self.armor * self.level
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# <__main__.Armor object at 0x7fc4800e2cf8>
Вот то же самое, но уже написанное с применением
dataclass
:from dataclasses import dataclass
@dataclass
class Armor:
armor: float
description: str
level: int = 1
def power(self) -> float:
return self.armor * self.level
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# Armor(armor=5.2, description='Common armor.', level=2)
Поддержка папок пакетов без файла __init__.py (3.3+)
Один из способов структурирования Python-кода заключается в использовании пакетов (пакеты размещаются в папках, в которых есть файл
__init__.py
). Вот пример из официальной документации:sound/ Пакет верхнего уровня
__init__.py Инициализация пакета sound
formats/ Подпакет для преобразования форматов файлов
__init__.py
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Подпакет для звуковых эффектов
__init__.py
echo.py
surround.py
reverse.py
...
filters/ Подпакет для фильтров
__init__.py
equalizer.py
vocoder.py
karaoke.py
...
При использовании Python 2 в каждой из папок, упомянутых в примере, должен был быть файл
__init__.py
. Благодаря этому файлу папка воспринимается в виде Python-пакета. В Python 3, с появлением возможности Implicit Namespace Packages, наличие в папках подобных файлов больше не является обязательным.sound/ Пакет верхнего уровня
__init__.py Инициализация пакета sound
formats/ Подпакет для преобразования форматов файлов
wavread.py
wavwrite.py
aiffread.py
aiffwrite.py
auread.py
auwrite.py
...
effects/ Подпакет для звуковых эффектов
echo.py
surround.py
reverse.py
...
filters/ Подпакет для фильтров
equalizer.py
vocoder.py
karaoke.py
...
Надо отметить, что на самом деле всё не так просто. А именно, в соответствии с этой официальной спецификацией, файл
__init__.py
всё ещё нужен для обычных пакетов. Если убрать его из папки, то пакет превращается в так называемый пакет пространства имён, к которому применяются дополнительные ограничения.Итоги
В этом материале рассмотрены далеко не все интересные возможности Python 3, но мы надеемся, что вы нашли здесь что-то полезное. Код примеров можно найти в этом репозитории.
Уважаемые читатели! Какие возможности Python 3 вы добавили бы в приведённый здесь список?

Комментарии (74)
dim2r
21.05.2019 17:16Python молодцы. Многие языки еще долго будут догонять.
Еще бы добавить аннотаций для функционального программирования и будет еще кручеevgenyk
21.05.2019 18:03Повторюсь. Я за то, чтобы сохранять ядро языка как можно более компактным. Чтобы его можно бы было всюду использовать с минимальными изменениями.
dim2r
22.05.2019 08:18По идее давно напрашивается модульный язык. Зачастую в энтерпрайзе надо чтобы программисты использовали одни и те же конструкции и не использовали какие-то другие. Надо научить язык отсекать лишние в конкретном проекте конструкции.
evocatus
21.05.2019 20:32В модуле typing вроде есть Callable? Т.е. функции высшего порядка вполне могут работать с этими аннотациями.
assembled
21.05.2019 21:19аннотаций для функционального программирования
Вы мне лучше скажите, какая аннотация должна быть у функции, принимающей совпадение по регулярке (тип _sre.SRE_Match)?
lega
21.05.2019 18:04+2Какие возможности Python 3 вы добавили бы в приведённый здесь список?
asyncio и холиварный:=
(3.8+)
smer44
21.05.2019 20:48что сказать, язык будущего))
очень классно было бы действительно по желанию включать проверку либо по названию класса либо структуры обьекта в рантейме и где можно на этапе интерпретации, а то перепутаешь места в функции, а ошибку выдаёт из чёрт знает какого места, а потом в коде хорошо протестенном отключать
и очень интересная фича LRU кэш, я чессно не знал и dict юзал
кста вроде одинакого по скорости этот кэш и ручной через dict, кто нибудь протестил нормально скорость??
имхо, в Питон версии 100500 + следует ввести:
- инспекцию структуры любого обьекта с преттипринтом или лучше в визуальном представлении, когда берёшь неизвестный модуль, где документация плохая а все типы стёрты
- обьединить нотацию для класса и dicta, inst['field'] эквивалентно inst.field
интерпретировать сложные comprehention декларативно с оптимизаций и кешем где надо
встроить без геморa частичные функции, с поддержкой интерпретатора заместо partial:
def fn ( a,b,c)
k = fn(1,2) #-> обьявление частичной функции k(c) c изсестными a,b,
потом
k[0] = 3 # безгеморная замена параметра на определённом месте
сложные lambda обьявления с функционалом как обычный метод
fib = lambda @lru_cache(maxsize=512), a: int ,b : int -> int : if number == 0: return 0
if number == 1: return 1
return fib_memoization(number-1) + fib_memoization(number-2)
kalininmr
21.05.2019 22:49кстати о f-string.
оно и раньше так же примерно работало
"Hello {user}!".format(**locals)
или **varslega
21.05.2019 23:11+1Все же не так красиво, да и по скорости f-string в разы* быстрее:
Тестировалось на python 3.7, оригинал.Type Trial 1 Trial 2 Trial 3 %: 0.273233943000 0.268914790000 0.273714235000 str.format(): 0.7942503730000681 0.793637686999773 0.7926878570001463 str.Template(): 3.3321329630002765 3.3256752329998562 3.315622544999769 f-string: 0.1914799450000828 0.18900782099990465 0.19004946999984895
onegreyonewhite
22.05.2019 03:49оно и раньше так же примерно работало
Нет, не так же. Можно было только подставлять значения, а делать вычисления прямо внутри было нельзя (теперь в f-string можно). Так что теперь нужно быть осторожнее с такими строками.
<sarkazm>
Это почти как маленький php внутри python.</sarkazm>
Ко всему + то что сказал lega производительность в разы больше.
Примерные тесты~ $ python3.7 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())' 500000 loops, best of 5: 620 nsec per loop ~ $ python3.7 -m timeit 'a = 1' 'b = "string"' 'f"Test strings formatting: {a} {b}"' 2000000 loops, best of 5: 157 nsec per loop ~ $ python3.6 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())' 1000000 loops, best of 3: 0.599 usec per loop ~ $ python3.6 -m timeit 'a = 1' 'b = "string"' 'f"Test strings formatting: {a} {b}"' 10000000 loops, best of 3: 0.142 usec per loop ~ $ python2.7 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())' 1000000 loops, best of 3: 0.435 usec per loop
splatt
22.05.2019 10:46Изменения крутые и полезные.
Очень хотелось бы что бы исправили концептуальные проблемы языка, даже если это означает python 4 и сломанную обратную совместимость.
Например те же импорты все ещё очень сложны для понимания новичками (особенно по сравнению с другими языками). При этом циркулярные импорты падают в рантайме с неадекватной ошибкой, а orm фреймворки вроде той же алхимии предлагают решать проблему просто — указывать название классов текстом *facepalm".
Tishka17
22.05.2019 12:21+1К сожалению, я изучал питон очень давно и не могу 100% вспомнить какие у меня были проблемы. Но, кажется, импорты — это не та вещь, которая мне показалась непонятной.
Про циклические импорты действительно не мешало бы добавить текст более явный, хотя если посмотреть на стек это и глазами довольно легко понять.
В ОРМ циклические завимости надо решать не написанием названий классов текстом, а правильной расстановкой relation, мне кажется это в 95% случаев возможно (если я не прав — прошу привести пример).
smer44
23.05.2019 00:14циклическая зависимость легко может разрешаться интерпретатором без жалобы на якобы ошибки, зря что так не встраивают. Странное дело её не разруливает даже maven в своих проэктах…
Tishka17
23.05.2019 08:47На мой взгляд, циклическая зависимость в большинстве — ошибка проектирования и должна исправляться переразбиением модулей или снижением связности кода (иногда — использованием DI, например).
lega
23.05.2019 09:39+1циклическая зависимость легко может разрешаться интерпретатором без жалобы на якобы ошибки
Как такая цикличекая зависимость может решиться интерпретатором?
# a.py import b value = b.value + 5 # b.py import a value = a.value + 7
PS: В питоне нормально решаются циклические зависимости, проблемы не в питоне, а «в руках».
igorp1024
24.05.2019 08:37её не разруливает даже maven в своих проэктах
Имхо, с ней намного проще получить в проекте одну и ту же зависимость с разными версиями. И долго искать причину фокусов в рантайме.
KonstantinSpb
22.05.2019 21:31LRU-кэш полезная штука, но надо пользоваться с умом, т.к. он подходит только для идемпотентных функций(методов)
evgenyk
Мне кажется, абсолютно необходимо упомянуть, что аннотации типов это не проверка типов, а подсказка компилятору.
gigimon
Не только компилятору, но и всяким IDE
evgenyk
Моя интуиция почему-то протестует против внесения в синтаксис языка фичей предназначеных для IDE. Слишком сильно связаны получаеются IDE и язык.
gigimon
а как комплиятор сейчас это использует? Насколько я читал, CPython пока никак не использует type annotation, а вот IDE уже вовсю
evgenyk
Сам компилятор в рантайме не использует, но можно подключить «runtime code evaluator» и он будет проверять данные в рантайме.
Ссылка: the state of type hints in Python
sehin_d
Использование аннотаций, помимо прочего, достаточно сильно повышает читаемость кода. Так что можно считать это не только фичей для IDE.
Xop
Есть еще анализатор mypy, который умеет проверять соответствие типов
0xd34df00d
Для произвольного кода?
Какова вообще выразительная сила тайпхинтов как системы типов?
evocatus
Там есть Any, Union (sum type), дженерики и пр. Т.е. можно описать любой тип, даже сложную вложенную структуру или функцию. Можно создать свой кастомный тип.
В общем Python списывает хорошее у Haskell ещё начиная со списковых выражений.
подробнее здесь: docs.python.org/3/library/typing.html
0xd34df00d
А там накладывать требования на типы можно? Ну, например, чтобы у меня дженерик был не поверх любого типа
T
, а поверх толькоU[T]
для фиксированного (или нет)U
?Тайпклассы, экзистенциальные типы, кусочки зависимых типов, kind polymorphism?
evocatus
Кажется, да.
Много можно чего ещё скопировать из Haskell (и раздуться похлеще монстра C++), но задачи такой, как я понимаю, не стоит. Я просто имел в виду, что разработчики Python в сторону Haskell смотрят внимательно и заимствуются фичи оттуда давно, потому что list comprehensions в Python с 2000 года.
noth
Python это не компилируемый язык.
Код python исполняется интерпретатором, а он в свою очередь все эти аннотации типов полностью игнорирует, что вы же в своём примере и продемонстрировали.
Аннотации это для чего угодно, но не для исполнения кода: для читаемости, для подсказок в IDE, для статического анализа (mypy тот же самый), etc.
evgenyk
Ну здравствуйте, вроде как всю жизнь код питон компилируется в промежуточный байт-код и этот байт код затем исполняется.
lorc
Ну объектные файлы же не продуцируются. Слинковать с другими объектными файлами ничего нельзя.
Байт-код все равно исполняется виртуальной машиной.
evgenyk
С точки зрения типов, компилятор мог бы проверять типы на этапе компиляции байт кода. Таким образом, можно проверять код до его запуска. У чистого интерпретатора, без этапа компиляции такой возможности нет, в случае ошибки типа он все-равно должен закончить выполнение.
Т.е. у чистого интерпретатора мало смысла в проверке типов.
Я то сам, дне думаю, что для динамического языка не стоит внедрять обязательную проверку типов. Ну если только в качестве опции в виде билбиотеки. Не в ядре.
Я за то, чтобы сохранять ядро как можно более компактным.
Peter03
В рамках стратегии Fail fast имеет смысл.
assembled
С такой логикой любой язык можно назвать интерпретируемым, ведь в конечном итоге всё интерпретируется процессором.
ratijas
При всём уважении к Вашему юзернэйму, это так не называется.
assembled
Не называется, но если следовать логике lorc (при которой если язык компилируется в нечто, что далее интерпретируется, то сам язык считается интерпретируемым), все языки можно назвать интерпретируемыми.
lorc
Нет, это не моя логика. Пожалуйста, не надо приписывать мне то, чего я не говорил.
assembled
А это чьи слова:
И это в ответ на утверждение, что питон компилируется. Из контекста я понял, что вы считаете его интерпретируемым. Или я ошибаюсь?
lorc
Да, я продолжаю считать питон интерпретируемым. И буду считать его таким, пока не смогу получить из программы на питоне объектный файл, скормить его ld и слинковать его с моей программой на С.
assembled
Из программы на питоне можно получить .pyc файл. И при этом ни строчки из этой программы не исполнить.
Al_Azif
Скажите, .pyc-файл исполняется непосредственно на процессоре, без вспомогательных прокладок?
Вот когда начнёт исполняться — тогда и будет компилируемым.
assembled
Но байткод-то в результате чего получается? В результате компиляции питонокода.
Машкод тоже может не исполнятся непосредственно на процессоре. В современных процах он собственно и не исполняется, а транслируется в микрокоды, которые затем исполняются.
igorp1024
В машинный.
p.s. Тут какой-то спор о терминах, которые уже давно устоялись.
lorc
Нет. Компилятор производит объектные файлы, которые потом линкуются в исполняемый файл. Который потом может быть загружен в память и исполнен процессором. Это вроде бы обычный способ определять компилируемый язык.
И нет, если вы запакуете виртуальную машину и байт-код в один файл, то это все равно не будет скомпилированной программой. Ваша программа (а не ВМ) должна быть переведена в машинные коды, которые смогут исполняться процессором целевой платформы.
assembled
Во-первых, компилятор не обязательно генерирует объектный файл, он может генерировать и файл с байт-кодом, или сразу исполняемый файл, или вообще простой бинарный файл являющийся сырым образом программы и её данных (ещё возможен вариант генерации программы на другом языке, но это принято называть транспилером).
Во-вторых, компилятор может вообще не генерировать никаких файлов, а компилировать программу в памяти, снаружи он будет подобен интерпретатору, но компилятором от этого быть не перестанет.
Здрасьте, а байткод откуда взялся, если не был скомпилирован?
Я бы называл питон компилируемым в байт-код. Но да, сам байткод при этом интерпретируется (как и машинный код).
Al_Azif
>>Я бы называл питон компилируемым в байт-код. Но да, сам байткод при этом интерпретируется (как и машинный код).
После последней фразы могу запостить только эту ссылку. Финиш, конечно:
www.youtube.com/watch?v=LLk9_EH6Pfo
assembled
Что тут для вас «поворот»?
bravmi
Эм в этом примере наоборот же получается, чистый питон игнорирует аннотации.
evgenyk
Ну да, я это и хотел показать. Что чистый питон не проверяет типы. Если бы проверял, он что-то бы сделал, например выбросил бы исключение.
menstenebris
Ну скажем так, это не настоящая проверка типов, но кое-где они проверяются. Например dataclass не создает параметр для __init__ у которого не проставлен тип данных или добавленная перегрузка overload, из того же пакета typing, вообще не работает. Очень надеюсь в будущих выпусках разработчики усилят это направление.
evgenyk
Для меня большой вопрос, нужно ли тащить статическую проверку типов в рантайм питона. Возможно просто я не сталкиваюсь с необходимостью статической проверки, но лично меня вполне устраивает дак тайпинг в питоне. Если нужна строгая типизация можно просто взять другой язык. Ну хотя бы тот же Cython. Или Си или C++.
Если все тащить в одну кучу получится еще один C++, точнее получим все недостатки C++, без его достоинств.
В моей практике ошибки с типами так редки, что ИМХО не стоит городить огород.
NeZanyat
Ну мне кажется что все таки неплохо иметь возможность при необходимости использовать типизацию (пусть даже если для того что бы она работала в рантайме надо подключить какой ни будь enforce) в основном для себя языке программирования.
evgenyk
В виде подключаемых библиотек я готов мириться. Пусть будет, для любителей. Но только не в ядре языка.
По моему это просто мода. Лично я накушался строгой типизации в C++. А так сначала можно затащить строгую типизацию в питон, а потом без дизайн паттернс ни шагу ступить нельзя будет.
menstenebris
Без аннотации типов командой больше 5 человек разрабатывать очень трудно. Совершенно непонятно что тебе пробросили через 3 функции. Или могу я вызвать чужой метод и отправить в него set вместо list. это нужно еще и чужой код просмотреть прежде чем использовать.
Ну и уж совсем мечты это нормально предсказание типов для LLVM и для компилятора в WebAssemly, а то питон сейчас жив одним ML
lega
строгаястатическая типизацияassembled
Статическая проверка не может быть в рантайме, она выполняется на этапе компиляции, проверки в рантайме называются динамическими.
Типизация в питоне, кстати, строже чем в большинстве скриптовых. Хоть функции и принимают любые объекты, но вольностей, типа складывания строки с числом он не позволяет, и неявных приведений типов (как в js — лишь бы что-то выполнить) он не делает.
0xd34df00d
Сравнивать значения разных типов он, однако, вполне себе позволяет.
assembled
Потому что это осмысленная операция, и, в отличие от всяких JS и PHP, питон не будет пытаться привести их к одному типу, т.е. для разных типов всегда будет False.
bolk
assembled
Ваше === — это костыль для замены == с его глупым поведением.
bolk
Невежа. Тьфу на вас.
assembled
Просвятите, какая необходимость иметь в вашем языке оператор ==?
bolk
Мой язык — русский, в нём нет этого оператора. Кто касается ПХП, то достаточно вспомнить историю языка — простая работа с вебом, данные из форм приходят нетипизированно.
0xd34df00d
Для разных типов должен кидаться экзепшон, потому что никакие два значения разных типов никогда не могут быть равны. Если я сравниваю значения разных типов, то у меня ошибка в логике. John Major equality — плохая штука.
Иными словами,
False
в3 == 4
иFalse
в3 == "4"
имеют принципиально разную семантику.assembled
Это позволяет избежать лишних проверок типов руками. Обычное дело для функций, принимающих параметры различного типа.
0xd34df00d
А зачем параметры различного типа сравнивать? Они же различного типа.
assembled
С динамической типизацией тип параметров заранее может быть не известен же.
0xd34df00d
Со статической в каком-то смысле тоже не обязан. Обычное гомогенное равенство вполне себе полиморфно.
evocatus
Clojure — интерпретируемый язык с динамической типизацией, но там есть библиотека spec, которая позволяет описать «схему» данных примерно как в Python, но в рантайме это всё проверяется на корректность. И также есть аннотации типов, которые позволяют указать тип Java и это влияет на производительность, потому что можно написать алгоритм, который будет работать только со списком Long, не задействуя рефлексию.
worldmind
не компилятору, а статическому анализатору
evgenyk
Наверное лучше вообще убрать и компилятор и статический анализатор и просто написать; «это просто подсказки».
GamePad64
Также, type hints можно использовать, когда нам нужна явная привязка названия переменной к её типу. Например, есть библиотека pydantic, которая использует type hint'ы в датаклассах для маршаллинга и валидации схемы данных. Мы перешли на неё с marshmallow, очень удобно.
Tishka17
Стоит отметить, что pydantic использует свои классы вместо питоновских dataclasses, что может ограничивать применимость. Я в свободное время пилю библиотечку, которая может быть применима для уже существующих датаклассов без необходимости как-то их менять.