В Linux стандартными средствами можно использовать часть оперативной памяти как диск. Для этого требуется указать тип монтирования tmpfs
в команде mount
mount -t tmpfs -o size=5G tmpfs /mnt/ramТеперь путь
/mnt/ram
можно использовать как обычный каталог. Для чего это может быть нужно?Single File Size: 30.0GbСкрипт для теста ↗️
ssd > ssd: 0:00:12.850 / 2.3Gb/s
sdd > ram: 0:00:06.453 / 4.6Gb/s
ram > ram: 0:00:06.995 / 4.3Gb/s
ram > sdd: 0:00:06.217 / 4.8Gb/s
Dir size: 32.7Gb, File count: 11127
ssd > ssd: 0:00:15.063 / 2.2Gb/s
sdd > ram: 0:00:08.486 / 3.9Gb/s
ram > ram: 0:00:08.032 / 4.1Gb/s
ram > sdd: 0:00:07.026 / 4.7Gb/s
Как получить путь к файлу текущего класса если метод получения пути находится в родительском классе в другом файле?
Например, представим такую ситуацию:
# module1.py ###Что покажет код:
class BaseCls:
@classmethod
def get_path(cls):
print(__file__)
# module2.py ###
from module1 import BaseCls
class MainCls(BaseCls):
pass
import module2Мы ожидаем что путь будет к файлу
module2.MainCls.get_path()
module2.py
, но переменная __file__
объявлена внутри файла module1.py
и поэтому будет указывать именно на него.module_name = module2.MainCls.__module__▫️ найти этот модуль в списке импортированных модулей
mod = sys.modules[module_name]▫️ получить значение переменной
__file__
filepath = mod.
__file__
Вся эта процедура, причём для любого типа объекта, есть в функции inspect.getfile()
. Так что наш метод должен выглядеть так:# module1.pyТеперь из вызов этого метода из класса
import inspect
class BaseCls:
@classmethod
def get_path(cls):
print(inspect.getfile(cls))
MainCls
найдёт путь к файлу module2.py
#tricks
Читать полностью…
Помните трик с заменой строки в консоли с помощью символа "\r"
? Рассказывал в этом посте.
Что будет если сделать несоклько таких символов? Мы вернёмся на несколько строк выше? Нет, так не сработает.
Как же сделать замену нескольких строк?
Для этого нужна хитрая комбинация символов: \x1b[1A\x1b[2K
Что это вообще за магия? Это специальный код для управления курсором консоли.\x1b
: экранирующий символ означающий что дальше следует специальный символ. Он же символ ESC[1A
: команда "поднять курсор вверх". Есть и другие команды для перемещения курсора в разные стороны.[2K
: удалить текущую строку. "2"
означает всю строку. Еще есть опции: "0"
- от курсора до конца, "1"
- от курсора к началу.
Итого, наша абракадабра значит - подняться на строку вверх и удалть всю строку.
Полный список специальных символов можно найти на этой странице.
Эти функции консоли широко используются. Например модуль colorama использует модификации для изменения цвета текста в консоли.
В демке на видео показана манипуляция тремя строками в консоли. Пишем три строки, удаляем три строки, и тд.
➡️ Код с демонстрацией
#triks
Как не передавать аргумент в функцию если она его не ждёт?
Как-то раз я делал модуль с функциями, которые вызывались как фоновые задачи. В основном они принимали чёткий список позиционных аргументов. И вот, в разгар разработки, пришла новая фича - в каждую такую функцию теперь передаётся Lock-объект. Он позволяет сделать выполнение этой функции синхронным на разных воркерах (как и положено любому локеру).
Но вот проблема, в новых функциях, где нужен локер, я его, конечно же, принимаю как аргумент. Но в старых функциях он не предусмотрен. Часто функции вообще без аргументов.
Какие варианты решения?
▫️ Добавить во всех функциях в аргументы **kwargs
. Это решит все проблемы. Строго говоря, это надо было сделать сразу. Теперь таски не будут падать из-за неизвестного аргумента. И теперь следует не забывать добавлять **kwargs
в новых функциях. Но что, если нет возможности изменять код? Тогда...
▫️Проверить, может ли функция принять аргумент с определённым именем. И если не может то не передавать. Это можно сделать с помощью стандартной функции inspect.signature
from inspect import signatureТеперь можно проверить, ожидает ли функция параметр с определённым именем
def func(x, y, z=True):
pass
sig = signature(func)
print(sig)
# <Signature (x, y, z=True)>
print(sig.parameters)
mappingproxy(OrderedDict([('x', <Parameter "x">), ('y', <Parameter "y">), ('z', <Parameter "z=True">)]))
print('lock' in sig.parameters)Финальный псевдокод
# False
from tasks import my_task, LockClassКонечно же, наличие этого имени не гарантирует что функция ожидает именно этот тип. Но это уже нюансы реализации 😼
from inspect import signature
task_kwargs = {}
lock = LockClass()
if 'lock' in signature(my_task).parameters:
task_kwargs['lock'] = lock
my_task(**task_kwargs)
Я нашел самый быстрый способ поднять свой независимый и бесплатный VPN
Сразу оговорка, платить придётся только за хостинг.
1️⃣ Покупаем сервер где-то на просторах интернета. Конечно же сервер должен находиться за пределами страны. Например я закупился на https://eurohoster.org/ (не реклама). Проверяйте лимиты по трафику, в идеале - без ограничений.
2️⃣ Ставим docker
sudo apt install docker.ioЕсли удобней с DockerCompose то ставим и его
sudo apt install docker-compose3️⃣ Ставим WG-EASY
WG_HOST
- внешний статичный IP вашего сервераPASSWORD
- придумайте пароль для WEB UIРепозитори на почитать для расширения кругозора.
▫️ Большой ликбез по экосистеме Python
https://github.com/brunocampos01/understanding-the-python-ecosystem
▫️Подборка самых популярных полезностей для веб разработчиков
https://github.com/ml-tooling/best-of-web-python
▫️Подборка библиотек для тех кто уважает типизацию в Python
https://github.com/typeddjango/awesome-python-typing
#basic #libs
Celery это крутой инструмент для очереди задач web-вашего приложения на Python.
Но его монополию всё больше притесняет другая библиотека - dramatiq.
По словам автора, эта библиотека лишена всех проблем Celery
If you’ve ever had to use Celery in anger, Dramatiq could be the tool for you.
Обычно далее я пишу примеры как можно быстро опробовать библиотеку. Но кода там прилично, поэтому я сделал репозиторий с примером приложения на Flask.
Что реализовано в этом приложении:
▫️Засетаплен dramatiq совместо с Flask с помощью flask-dramatiq
▫️Отправка простых фоновых задач
▫️Отправка периодических cron-задач по расписанию с помощью periodiq
▫️Отмена запущенных задач с помощью dramatiq_abort
▫️Задача с отложенной проверкой результата
▫️Всё закатано в Docker, поэтому заводится с полпинка
В качестве брокера используется Redis. Он стартует быстрей чем RabbitMQ и dramatiq_abort пока что поддерживает только его. Шаблонов нет, фронт максимально глуп и минималистичен.
🌎 Код смотрим здесь https://github.com/paulwinex/flamatiq
Также стоит посмотреть:
dramatiq_dashboard
django_dramatiq
#libs
Помогая окружающим, лучше всего делать то, что умеешь делать лучше всего! Поэтому я решил сделать все свои курсы бесплатными и выложить в открытый доступ❗️
Был период в моей жизни, когда эти курсы помогли мне выжить. Теперь, я надеюсь, они помогут кому-то еще.
Спасибо всем, кто покупал мои курсы, даже если потом не смотрел 😁
Навыки программирования нынче оцениваются достаточно высоко, это шанс найти работу с зарплатой повыше, попросить повышения или просто улучшить свою продуктивность с помощью кодинга! Так что пользуйтесь 😉
Сразу скажу, курсы не свежие, записанны в 2014-2015 гг. Некоторая информация устарела но примерно на ±90% всё еще актуально.
Какие курсы доступны:
▫️ Advanced Python Scripting
Это компиляция из 3х курсов начального, среднего и высокого уровня сложности. С нуля до создания собственных десктоп-приложений.
▫️ Python for Maya
Курс для Python-разработчиков в Maya. Да, Autodesk ушел от нас, но скорее всего, вернётся)
▫️ Python for Nuke
Курс для Python-разработчиков в Nuke.
▫️ Python for Houdini
Курс для Python-разработчиков в Houdini. Самый отстающий в актуальности курс, так как Houdini развивается и меняется очень стремительно и кардинально. Тем не менее, 80-90% курса актуальна.
▫️ Houdini Fundamentals
Курс поможет познакомиться с крутым софтом для 3D графики — SideFX Houdini. Записан на версии 16. Рассчитан на нулевой уровень подготовки.
На этот раз курс не про Python, только про сам софт.
Все Python-курсы записаны с Python2, вам потребуется изучить особенности перехода на версию Python3, о чем я не раз говорил на своём канале. Ищите по хэштегу #2to3
💬 Остальные подробности в комментах
Переходите на сайт школы и выбирайте курс:
➡️ https://cgninjas.ru/ ⬅️
📌 @pythonotes
#offtop
⚠️ События последних дней сильно изменили жизнь многих из нас. Мы уже не будем прежними и мир вокруг не будет прежним.
Лично я всё ещё в некотором тупняке от происходящего...
Но этот канал не о политике и здесь я не буду рассуждать кто прав а кто виноват. Для нас сейчас главное — пережить пик кризиса и адаптироваться к новым реалиям. Обязательно беречь себя и помогать окружающим.
В свою очередь я хочу помочь всем, кто меня знает и читает. Поэтому у меня к вам есть три предложения...
#offtop
Метод строки split()
разделяет строку на несколько строк по указанному символу
>>> "a_b_c".split('_')Можно указать максимальное количество разделений
['a', 'b', 'c']
>>> "a_b_c".split('_', 1)Или резать с другой стороны с помощью
['a', 'b_c']
rsplit()
(right split)>>> "a_b_c".rsplit('_', 1)А что будет если оставить аргументы пустыми?
['a_b', 'c']
>>> "a_b_c".split()Получаем список с одним элементом, потому что по умолчанию используется пробельный символ.
['a_b_c']
>>> "a b c".split()То есть это равнозначно такому вызову?
['a', 'b', 'c']
>>> "a b c".split(" ")Кажется да, но нет! Давайте попробуем добавить пробелов между буквами
['a', 'b', 'c']
>>> "a b c".split(" ")И вот картина уже не так предсказуема 😕
['a', '', '', 'b', '', '', 'c']
>>> "a b c".split()Всё снова красиво! 🤩
['a', 'b', 'c']
>>> "a\t b\n c ".split()Аналогичный способ можно собрать с помощью регулярного выражения. Но пробелы по краям строки придется обрабатывать дополнительно.
['a', 'b', 'c']
>>> import reЗдесь тоже можно указать количество разделений
>>> re.split(r"\s+", ' a b c '.strip())
['a', 'b', 'c']
>>> re.split(r"\s+", 'a b c', 1)А что если мы хотим написать красиво, то есть
['a', 'b c']
split()
без аргументов, но при этом указать количество разделений? В этом случае первым аргументом передаём None
>>> "a\n b c".split(None, 1)Данный метод не учитывает строки с пробелами, взятые в кавычки
['a', 'b c']
'a "b c" '.split()Но для таких случаев есть другие способы.
['a', '"b', 'c"']
Ещё пара триков с форматированием!
▫️Порядки больших чисел можно разделять запятой (и только запятой)
>>> '{:,}'.format(1_231_312)▫️Если оставить пробел в форматировании
'1,231,312'
float
после ":
" то он добавится в строку>>> '{: .2f}'.format(15)Но если подать отрицательное число, то знак минуса займёт этот пробел
' 15.00'
>>> '{: .2f}'.format(-15)Удобно для формирования строк одинаковой длины независимо от знака числа.
'-15.00'
😋 Заметка от читателя @nencoru
Как смержить несколько файлов с отсортированными строками в один файл, тоже отсортированный?
🔸 Исходный файл:
▫️ в одной строке находится одна запись
▫️ запись содержит предсказуемое поле с некими данными для сортировки. Например, логи с указанием времени.
▫️ все записи в файле отсортированы по этому полю
🔸 Задача:
Смержить несколько таких файлов так, чтобы в финальном файле все записи были также отсортированы.
🔸 Решение 1:
Предположим, у меня JSON-логиfiles = ['file1.jsonl', 'file2.jsonl', ...]
Усложним задачу — размер каждого файла 5Gb 😱
data = []
for file in files:
with open(file) as f:
data.expand(
f.readlines()
)
data.sort(key=lambda line: json.loads(line)['timestamp'])
with open('merged.jsonl', 'w') as f:
f.writelines(data)
Это означает, что вам потребуется оперативной памяти 5*len(files) Gb.
И уже не каждый компьютер сможет смержить 3-4 таких файла. А если их 100?
fileinput может помочь написать более красивый код, но с памятью не поможет.
🔸 Решение 2:
Можно использовать готовую функцию heapq.merge() из стандартного модуля heapq!
Heap - это бинарное дерево, где каждый родительский элемент в дереве имеет значение меньшее чем дочерний.
То есть, по умолчанию все элементы как-либо отсортированы.from heapq import merge
А учитывая, что merge это генератор, умеет работать с файлами и ему можно передать функцию для сортировки, он отлично подойдет для нашей задачи, так как полной загрузки в память не происходит!
items = [
[3,2,6],
[1,5,4]
]
print(list(merge(*items)))
# [1, 2, 3, 4, 5, 6]
И тут вы спросите: что за магия?
Тоже самое только без расхода памяти? Волшебный генератор всех спасёт?
Нет, за всё приходится платить. В случае с heapq весь процесс драматически замедлится.
Но задача будет выполнена! 😎
Сделал для вас синтетический пример для генерации и мержа подобных файлов
Код смотреть здесь ↗️
▫️ make_logs() генерит 30 файлов по 50Mb для теста
▫️ merge_list() мержит файлы через простой список
▫️ merge_heapq() мержит файлы через heapq
▫️ memory_profiler считает используемую память (нужно установить модуль)
▫️ также есть замер времени
Кому лениво там же смотрите мои тесты:
1.5Gb и 19.5с против 19Mb и 2м 43с
памяти в 80 раз меньше, но времени в 8 раз больше
⚠️ ВАЖНО
для чистоты эксперимента запускать следует из консоли и по одному тесту на процесс. То есть закоментили второй, запустили первый, закоментили первый, запустили второй.
#bacik
🤟😜🍺🎁🎉🍻🎂
Поздравляю всех 3Dшников с проф-праздником!
PS. А еще на канале юбилейный пост под номером 300! 😎
#offtop
Вышла альфа версия Python 3.12. Можно оценить что нам готовят.
Полезно глянуть что будет удалено в 3.13. distutils
уже удалили.
Еще можно посмотреть стрим на час про новинки.
#release
Сделал простой скрипт для управления температурой кулера Raspberry Pi.
- Зачем? Их же полно готовых! Даже дефолтные инструменты есть.
Хотелось сделать этот велосипед по-своему 😁
▫️Пороговых температур две: Одна контролирует момент включения, другая выключения. Например, выше 80 - включаем. Остыл до 50 - выключаем.
▫️ Значения пороговых температур заипсывается в специальном файле и считывается каждый раз при проверке темературы без рестарта сервиса.
Исходники и схема.
https://github.com/paulwinex/rpi_fan_control
ЗЫ. В схеме присуствует сопротивление. Это требуется только если нужно уменьшить скорость вращения кулера. Мой на полных оборотах сильно гудит, пришлось поставить.
#sources #rpi
Релиз Python 3.11 В прямом эфире
▶️ https://www.youtube.com/watch?v=PGZPSWZSkJI
▶️ Подробности по релизу
https://docs.python.org/3.11/whatsnew/3.11.html
Приветствую подписчиков.
Я даже несколько удивлён как это вы еще не разбежались 😻))
Многие мне пишут в личку с вопросом "Где брать курсы если сайт закрылся?".
Отвечаю — сайт не закрылся❗️
Ввиду некоторой обстановки 💣⚔️🚓 💥 мой хостинг (Украинский кстати) немножко "закончился".
Мой хороший знакомый, админ данного хостинга, включил мой сервер на пару часов и я скачал все данные. После чего создал новый сервер в Европе и поднял там сайт.
На данный момент я закончил настройку и теперь сайт снова доступен.
А так же добавил домен COM, для тех у кого RU блокируют.
Теперь сайт доступен по адресам:
https://cgninjas.ru/
https://cgninjas.com/
(возможно на com почта не будет работать)
Также просят выложить всё на Youtube или на Torrent. Да я не против, только исходники у меня пропали вместе с HDD, так что надо потратить немало времени чтобы скачать, систематизировать и залить. Если вдруг выкрою недельку на это, то обязательно сделаю)))
▫️Погодите, а что с каналом? он тоже всё?
Нет, ввиду всё тех же событий 💥 потух творческий запал, и мотивация пропала...
Но идеи есть, заготовки тем тоже. Возможно, я жду хороших новостей.
Обязательно продолжу серию заметок... надеюсь скоро. А до тех пор я не хочу вас раздражать глупой рекламой глупых каналов (которые запрудили телеграм и предложения которой приходят регулярно) а лучше просто помолчу.
"Благодарим за понимание ©"
ЗЫ. Не пишите пжлст в личку, есть чат для этого
➡️ CGNinjasChat
#offtop
Блокировки, они повсюду...
Ох как надоела эта тенденция. Наша IT индустрия буквально стреляет себе в ногу силами определённых личностей и их мнений о том как для нас будет лучше.
Как бы там ни было, сейчас все советуют экстренно качать и переносить свои видосы с YouTube на отечественные аналоги.
Да, можно использовать оконные софты со всеми удобствами (VLC или Youtube Video Downloader), но мы лучше покодим😉
Я набросал небольшой скрипт для скачивания плейлиста с YouTobe в один клик.
Из зависимостей только модуль pytube
.
▫️ Для скачивания выбирается максимально доступный размер видео файла.
▫️ Если файл уже существует то скачивания не будет. Удобно для апдейта обновлений.
▫️ Скрипт качает всё из плейлиста с помощью класса pytube.Playlist
. Если хотите скачать канал, то просто замените класс на pytube.Channel
from pytube import ChannelЗабираем здесь ➡️
PLAYLIST_URL = 'https://www.youtube.com/channel/XXXXXXXXX'
playlist = Channel(PLAYLIST_URL)
Раньше на курсах была проверка домашних заданий и
фитбек. Теперь, по понятным причинам, я не смогу это поддерживать в том же объёме.
Чтобы вам не учится в сферическом вакууме, я сделал для вас площадку для общения. А точней две!
Там вы сможете помогать друг другу, ведь старые студенты тоже подтянутся, за что я буду им очень благодарен.
Я тоже буду отвечать на ваши вопросы по возможности.
Основная площадка:
➡️ Discord Сервер
Запасной вариант:
➡️ Telegram Чат
👍 Вход свободный, все чаты открыты.
🤬 Никакой политики и токсичности! Только по делу!
💣 Нарушителей банить буду резко и с размаху!
📌 @pythonotes
#offtop
Из-за всем известных событий очень многие потеряли работу.
Для поиска вакансий вполне можно использовать паблики в telegram. Вот несколько которые мне известны:
/channel/django_jobs
/channel/javascript_jobs
/channel/workzavr
/channel/workoo
/channel/Workesss
@g_jobbot
➡️ Чем шире о себе заявите, тем больше шансов найти нужный контакт. Поэтому предлагаю айтишникам и художникам бесплатно разместить на моём канале @pythonotes информацию о вас.
Формат сообщения можно сделать примерно следующий:
_______________________________
Имя Фамилия
Специализация
О себе
- Долго думаю, быстро делаю.
Кем хочу работать
- Разработчик мобильных приложений
Локация
- Удалённо, возможен переезд в ГородНейм
Знаю языки программирования
- JSON
- CSS
- HTML
Хорошо владею софтом
- Maya. Ротоскопинг, трекинг
- Nuke. Персонажная анимация
- 3DsMax. Композитинг и кленап
Где работал
- Microsoft, админ лифта
- Yandex, доставка пончиков
- Disney, протирка шариков от мышей
Контакты
- Телеграм: @username
- Почта: user@name.com
- Полное резюме (ссылка на GoogleDoc/LinkedIn/PDF)
_______________________________
Картинки не надо, смайлы без фанатизма.
Текст присылайте в этот временный канал, где будем обсуждать все вопросы:
▶️ @pn_work
🌼 Если найдутся желающие, вакансии тоже могу запостить
📅 Предложение актуально как минимум до лета 2022г.
Если будет хоть один пост, уже не зря старался)
📌 @pythonotes
#offtop
Сегодня будет самый "двоичный" ("двойковый"? "двушный"? "двойственный"?) момент на вашем веку 🤩
Больше двоек в дататайме вы не застанете!
Успейте поймать момент! Будете показывать эпичный скриншот своим внукам)))
🥸 Для продуманных (ленивых): код на скрине, который сработает только сегодня и только 1 раз!
⏱ Открывайте окошки с часами и вперёд!
#offtop
Синтаксис f-string позволяет использовать в строке символ обратного слеша "\
" но не позволяет использовать его внутри фигурных скобок.>>> lines = ['line1','line2']
Решения:
>>> print(f'ITEMS: \n {"\n".join(lines)}')
SyntaxError: f-string expression part cannot include a backslash
▫️ вынести этот символ за скобкиn = '\n'
▫️ заменить его на другое представление, например создавать нужный символ с помощью функции
print(f'ITEMS: \n{n.join(lines)}') chr()
print(f'ITEMS: \n{chr(10).join(lines)}')
Чтобы узнать код символа следует использовать функцию ord()
А с юникодом не выйдет, там тоже слеш
>>> ord('\n')
10>>> print(f'ITEMS: \n{"\u000a".join(lines)}')
#tricks
SyntaxError: f-string expression part cannot include a backslash
При форматировании числа в строку можно задать паддинг - заполнение нулями до нужной длины.
>>> '{}_v{:05d}'.format('name', 125)А что если паддинг тоже задан переменной? В таком случае мы можем добавить форматирование этой переменной внутри формата первой. Порядок переменных следует указывать в порядке появление открывающейся скобки.
'name_v00125'
>>> '{}_v{:0{}d}'.format('name', 125, 5)Или вот так
'name_v00125'
>>> '{}_v:{:{:02d}{}}'.format('name', 125, 5, 'd')А можно глубже?
'name_v00125'
ValueError: Max string recursion exceededНо никто не запрещает использовать один тип форматирования внутри другого.
>>> ('{}_v{:{:%0.2dd}{}}' % 2).format('name', 125, 5, 'd')С f-string можно использовать 4 уровня, по количеству доступных типов кавычек. Можно, но не нужно 😕
'name_v00125'
f'''{f"""{f'{f"{x}"}'}"""}'''#tricks Читать полностью…
Наверняка вы замечали, что в Python есть удобная функция для получения переменной окруженияos.getenv(NAME)
И её "сестра" для создания или изменения переменных окруженияos.putenv(NAME, VALUE)
Но почему-то putenv()
не работает как должно. Энвайромент не обновляется!os.putenv('MYVAR', '1')
... и ничего 😴
print(os.getenv('MYVAR'))
Почему так?
На самом деле энвайромент обновляется, но это значение не добавляется в словарь os.environ
.
Откройте исходник функции os.getenv()
. Это просто шорткат для os.environ.get()
В то время как putenv()
это built-in С-функция.
Словарь os.environ
(или точней класс из MutableMapping) создаётся из энвайромента в момент инициализации. Функция putenv()
самостоятельно его не изменяет.
В тоже время, когда вы создаёте или изменяете ключ в os.environ
, автоматически вызывается putenv()
в методе __setitem__().
То есть, технически putenv()
всё делает верно, но в os.environ
это не отражается. Можно проверить так:>>> os.putenv('MYVAR', '123')
Я объявил переменную в текущем процессе и вызвал дочерний процесс, который её унаследовал и получил в составе
>>> os.system('python -c "import os;print(os.getenv(\'MYVAR\'))"')
123os.environ
.
Аналогично при удалении переменной вызывается еще одна built-in функция unsetenv()
, удаляющая переменную из системы.
Итого
▫️ Удобней всего явно обновлять переменные через os.environ
▫️ Есть способ неявно создать/удалить переменную через putenv
/unsetenv
, что не повлияет на os.environ
но изменит энвайромент и передаст изменения сабпроцессам. Но так лучше не делать!
▫️ os.environ
это просто обертка для built-in функций putenv()
и unsetenv().
#basic
Если часто работаете с архивами, то наверняка знакомы с модулем zipfile.
Нет ничего сложного добавить директорию в архив.
import zipfileДовольно немногословно. Но можно короче! В модуле
from pathlib import Path
dir_name = '~/input_files'
zip_name = '~/archive.zip'
with zipfile.ZipFile(zip_name, 'w') as zip:
for file in Path(dir_name).glob('**/*'):
zip.write(file, file.relative_to(dir_name).as_posix())
shutil
уже есть готовый методdir_name = '~/input_files'А что насчет распаковки?
zip_name = '~/archive'
zip_file = shutil.make_archive(zip_name, 'zip', root_dir=dir_name)
zipfile
это одна строкаzip_file = '~/archive.zip'Ну и тем более в
out_dir = '~/out_dir'
zipfile.ZipFile(zip_file).extractall(out_dir)
shutil
shutil.unpack_archive(zip_file, out_dir)В примерах не делается expanduser для краткости
shutil
более универсальна, так как второй аргумент format
задаёт алгоритм сжатия, от чего зависит выбор библиотеки. Если написать формат tar
, то вместо zipfile
будет использоваться tarfile
.zip, tar, gztar, bztar, xztar
. Но только если на текущем хосте доступны соответствующие библиотеки.shutil.make_archive()
там отсутствует shutil.unpack_archive()
. А появился он только в 3.7! Не очень понятно почему, но это еще один повод переходить на Python3 😊