Есть ли отличия в асинхронном и многопоточном коде в контексте Python?
Если нужно конкурентно делать блокирующиеся вызовы, без потоков просто не обойтись. Асинхронность работает только тогда, когда вся цепочка вызовов от начала до конца асинхронная.
На настоящей многопоточности можно полностью параллельно исполнять не-Python код, например, долго работающие вызовы математических библиотек. Или адапторы к базе данных часто не имеют асинхронной версии. С другой стороны, настоящие потоки довольно дорогие с точки зрения таких ресурсов, как стек и объекты ядра, что ограничивает их количество примерно 1000, но вообще лучше не более 50 без необходимости