Установка:
pip install aiofiles
Импорт
import aiofiles
Aiofiles(документация) — отличная библиотека для асинхронного скачивания медиа-файлов из интернета. С её помощью мы можем ускорить загрузку файлов и чтение больших текстовых файлов практически в 10 раз. В этом разделе курса мы сравним синхронные и асинхронные способы скачивания изображений и видео с нашего сайта-тренажёра, чтобы наглядно продемонстрировать разницу в скорости.
Aiofiles может работать не только с файлами, которые хранятся на вашем компьютере. Прелесть работы с этой библиотекой заключается в том, что она умеет работать с response.content, а это именно то, что нужно для создания асинхронной загрузки файлов.
Функция open() из aiofiles работает почти так же, как и привычная всем синхронная функция open(), встроенная в Python. Мы можем использовать асинхронную версию функции open() как с менеджером контекста with, так и без него.
С использованием менеджера контекста with:
import aiofiles import asyncio #С использованием менеджера контекста with async with aiofiles.open('filename', mode='r') as f: contents = await f.read() print(contents) #Результат 'My file contents'
Без использования менеджера контекста with
file = await aiofiles.open('filename', mode='r') contents = await file.read() file.close() print(contents) #Результат 'My file contents'
При использовании асинхронной функции open() из библиотеки aiofiles необходимо закрывать файл с помощью file.close(), если мы не использовали контекстный менеджер with. С применением контекстного менеджера with файл закроется самостоятельно после прочтения.
Асинхронная функция open() имеет такие же атрибуты, как её синхронный аналог.
aiofiles.open(‘folder/file.txt’) — абсолютное или относительное значение пути к файлу;
aiofiles.open(mode=») — необязательный атрибут, который указывает режим работы с файлом
- mode=»r» — открывает файл только для чтения, установлен по умолчанию;
- mode=»w» — открывает файл для записи, существующий файл будет перезаписан, а если файл не существует, то он будет создан;
- mode=»x» — бросает исключение FileExistsError, если файл с таким именем уже существует;
- mode=»a» — открывает файл для добавления данных в конец файла;
- mode=»t» — символ текстового режима;
- mode=»b» — символ двоичного режима, для записи медиа-файлов;
- mode=»wb» — открывает файл для записи в бинарном режиме, именно этот режим мы будем использовать для скачивания медиа файлов;
aiofiles.open(buffering=-1) — не обязательный аргумент, используется для политики буферизации;
- buffering=-1 — значение по умолчанию;
- buffering=0 — построчная буферизация, только для бинарного режима;
- buffering=1 — построчная буферизация для текстового файла;
encoding=’utf-8′ — необязательный аргумент, используется для кодирования или декодирования файла, этот аргумент следует использовать только для текстового файла;
errors=None — None по умолчанию, необязательный аргумент, указывает, как должны обрабатываться ошибки кодирования и декодирования, только для текстовых файлов;
- errors=»strict» — бросает исключение ValueError, то же самое что и None;
- errors=»ignore» — игнорирует ошибки кодирования, данные могут быть потеряны;
- errors=»surrogateescape» — любые некорректные байты будут представлены как символы Юникода, при обратной операции символы будут преобразованы обратно в байты;
- errors=»xmlcharrefreplace» — символы, которые не поддерживаются указанной кодировкой, будут заменены соответствующей ссылкой на символ XML
newline=’None’ — None по умолчанию, определяет работу режима новой строки, только для текстовых файлов;
newline=’\n’:
- При чтении: Каждый символ \n в файле интерпретируется как конец строки. Это означает, что файл разделяется на строки по каждому встреченному \n. Символы \r читаются как обычные символы и не влияют на разделение строк.
- При записи: Каждый символ новой строки в строке данных (независимо от того, \n или \r\n) преобразуется в \n перед записью в файл.
newline=’\r’:
- При чтении: Каждый символ \r в файле интерпретируется как конец строки. Это означает, что файл разделяется на строки по каждому встреченному \r. Символы \n читаются как обычные символы и не влияют на разделение строк.
- При записи: Каждый символ новой строки в строке данных преобразуется в \r перед записью в файл.
newline=’\r\n’:
- При чтении: Файл будет разделен на строки на основе последовательности символов ‘\r\n’. То есть каждый раз, когда встречается последовательность ‘\r\n’ в файле, она интерпретируется как конец строки. Если в файле встречаются отдельные символы ‘\n’ или ‘\r’, не составляющие полную последовательность ‘\r\n’, они будут восприниматься как обычные символы текста и не будут вызывать разделение строки.
- При записи: Каждый символ новой строки в данных, которые вы пишете в файл, будет преобразован в последовательность ‘\r\n’. Это означает, что если ваш текст содержит \n (стандарт для Unix и Linux систем), он будет автоматически преобразован в стиль новой строки Windows (\r\n), когда вы записываете его в файл.
closefd=True — True по умолчанию, если установить False, то файловый дескриптор не будет закрыт даже после команды file.close();
loop=None — None по умолчанию, если loop отсутствует, будет выбран event loop по умолчанию в соответствии с установленной политикой asyncio.
executor=»name_loop» — принимает экземпляр класса event loop, по умолчанию None, будет выбран цикл событий по умолчанию name_loop = asyncio.get_event_loop();
Aiofiles имеет в своём арсенале асинхронные копии привычных для всех функций чтения и записи файлов: а именно, readline, readlines, write и writelines. О них мы и поговорим ниже. На самом деле функций больше, и познакомиться с ними можно в документации. Для наших целей хватит и перечисленных. Все эти функции должны быть использованы с ключевым словом await, потому что, когда вы работаете с файлами, в момент чтения или записи цикл событий будет переключать контекст для выполнения других задач.
await file.writelines(str) — записывает в файл указанную последовательность строк.
#Этот код генерирует и записывает в файл построчно 100к рандомных чисел от 10к до 100к import time import aiofiles import asyncio import random async def write_numbers(): async with aiofiles.open('one_millon_numbers.txt', mode='a') as file: for x in range(1, 100001): await file.writelines(str(random.randint(10000, 100000)) + '\n') start = time.perf_counter() asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(write_numbers()) print(time.perf_counter() - start) Результат: 8.32808830001159
await file.write(str) — записывает в файл указанную строку.
# Этот код генерирует и записывает в файл целиком миллион чисел от 100к до 1кк import time import aiofiles import asyncio async def write_numbers(): async with aiofiles.open('async_write_one_millon_numbers.txt', mode='w') as file: numbers = str([x for x in range(100000, 1100001)]) await file.write(numbers) start = time.perf_counter() asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(write_numbers()) print(time.perf_counter() - start) Результат: 0.1362756000016816
await file.readlines() — cчитывает из файла все строки в список и возвращает его. Для запуска кода ниже и следующего вам понадобится файл, сгенерированный прошлым кодом.
import time import aiofiles import asyncio # Этот код читает файл построчно миллион строк. async def gen_numbers(): async with aiofiles.open('async_one_millon_numbers.txt', mode='r') as file: text = await file.readlines() print(text) start = time.perf_counter() asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(gen_numbers()) print(time.perf_counter() - start)
await file.readline() — cчитывает из файла одну строку и возвращает её.
import time import aiofiles import asyncio # Этот код читает из файла всего одну строку. async def gen_numbers(): async with aiofiles.open('async_one_millon_numbers.txt', mode='r') as file: text = await file.readline() print(text) start = time.perf_counter() asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) asyncio.run(gen_numbers()) print(time.perf_counter() - start)