TGTGInsighttelegram intelligenceLIVE / telegram public index
Назад кон каналите
Python Заметки avatar

TGINSIGHT CHAT

Python Заметки

@pythonotes

Education

Интересные заметки и обучающие материалы по Python Контакт: @paulwinex ⚠️ Рекламу на канале не делаю!⚠️ Хештеги для поиска: #tricks #libs #pep #basic #regex #qt #django #2to3 #source #offtop

Претплатници2,220Тековни претплатници
Следени објави384Број на индексирани објави
Неодамнешен опфат20,990Збир на неодамнешни прегледи
Неодамнешни објави

Неодамнешни објави

Ознака: #tricks · 178 објави

当前筛选 #tricks清除筛选

Објавено 26 јул.

В стандартном модуле random есть две очень похожие функции random.randint() random.randrange() Обе возвращают случайное значение из указанного диапазона >>> random.randint(10, 20) 12 >>> random.randrange(10, 20) 17 В чем же отличие? Дело в том что у randrange() есть третий параметр step. randint() действительно возвращает случайное число из указанного диапазона. randrange() на первый взгляд делает тоже самое если передать также два параметра. Но есть указать еще и step то наш диапазон усложняется, то есть в него попадёт не полный ряд значений. Например, я хочу получить случайное значение из диапазона но только чётное число. Тогда достаточно сделать так: >>> randrange(10, 20, 2) 16 Таким образом получается что randint это частный случай randrange без указания параметра step. Еще одно важное отличие в том, что randint() включает в диапазон второе значение а randrange() нет. То есть выражение randrange(10, 20) никогда не вернёт 20, а randint(10, 20) вернёт. #tricks#basic

1,950 views

Објавено 14 јул.

Наверняка у многих возникло желание потестить отправку сообщений, но Redis не установлен и вообще непонятно как с ним быть. На самом деле запустить его очень просто. 🔸 Для Windows, качаем архив, запускаем redis-server.exe 🔸 Для Linux несколько команд 🔸 Если есть Docker, то еще проще docker run --rm -p 6379:6379 redis Всё, можно экспериментировать! ______________________ А еще с сервером можно поиграть в пинг-понг > redis-cli ping PONG #tricks#libs#linux

1,890 views

Објавено 9 јул.

От многопоточных вычислений переходим к распределённым. То есть вычисления, происходящие на нескольких компьютерах. Конечно, в зависимости от задачи, вы можете взять готовые решения вроде CGRU или Deadline для рендеринга, charm4py или Dask для ML, или замутить что-то на AWS С2. Но хотелось бы чего-то попроще, попитоничней что ли) А ведь в Python есть средства "из коробки" для синхронизации нескольких процессов на разных хостах. Вот простой пример кода, который синхронизирует работу двух процессов на разных компьютерах. В этом случае используется процесс-посредник, который является синхронизирующим сервером. В примере создаётся некий Manager, который шарит общую для клиентов очередь. Все подключившиеся могут что-то в неё писать или забирать. В моём коде один процесс что-то "считает" и складывает в очередь, другой забирает и продолжает какие-то свои "расчёты". Если у вас есть несколько машин, то можете попробовать это запустить по сети (нужно заменить 'localhost' на IP-адрес сервера). Но и на локальной машине сработает. Gist 🌎 #libs#source#tricks

1,720 views

Објавено 2 јул.

🔖 Подводя итоги по прошлым постам плюс пара заметок: 🔸 Если требуется проверять идентичность содержимого архивов, лучше использовать ZIP и проверять только CRC. 🔸 Указанный способ может помочь проверить отдельные файлы в архиве с возможностью перекачать только новые а не весь архив 🔸 Для проверки хеш-суммы файла можно использовать утилиту md5sum (она не умеет проверять хеш внутри ахрхивов) # Linux: md5sum filename # Windows: python md5sum.py filename (находится в директории скриптов /Tools/scripts/md5sum.py) #libs#tricks

1,590 views

Hashtags

Објавено 30 јун.

На самом деле архивы TAR оказались менее удобными в нашей теме проверки идентичности. Давайте сделаем всё тоже самое для ZIP. Допустим, тестовые файлы мы уже создали используя код из прошлого поста. Теперь создадим архивы. import zipfile def create_zip(archive_path, files): with zipfile.ZipFile(archive_path, "w") as zf: for file in files: zf.write(file) create_zip('archive1.zip', files_to_archive) create_zip('archive2.zip', files_to_archive) Проверим хеш >>> hashlib.md5(open("archive1.zip", "rb").read()).hexdigest() 'd54670be5e01e483797ee4ae30089423' >>> hashlib.md5(open("archive2.zip", "rb").read()).hexdigest() 'd54670be5e01e483797ee4ae30089423' Отлично! ZIP создаёт одинаковые архивы и сразу выдаёт одинаковую хеш-сумму! Ну всё, на этом расходимся... Хотя подождите ка, часто ли вы проверяете один и тот же файл на идентичность? Давайте имитируем ситуацию когда файл был перезаписан или "модифицирован" но при этом фактически не изменился. То есть изменились только его атрибуты. Для этого можно использовать Linux-команду touch, которая обновляет время последнего доступа к файлу. touch example_file0.txt touch example_file1.txt ... Либо альтернативу на Python from pathlib import Path for f in files_to_archive: Path(f).touch() Содержимое файлов не изменилось! Но изменились атрибуты. Пересоздаём второй архив. create_zip('archive2.zip', files_to_archive) Проверяем >>> hashlib.md5(open("archive1.zip", "rb").read()).hexdigest() 'd54670be5e01e483797ee4ae30089423' >>> hashlib.md5(open("archive2.zip", "rb").read()).hexdigest() 'aa508dbba4e223abe45e16dba4ad6e1f' Вот это более правдивая ситуация. Давайте теперь сделаем функцию для проверки файлов внутри архивов, которая считывает непосредственно данные файлов в разжатом виде. def get_hash_zip(path): hash_md5 = hashlib.md5() with zipfile.ZipFile(path, "r") as z: for f_name in z.namelist(): with z.open(f_name) as f: hash_md5.update(f.read()) return hash_md5.hexdigest() Сравним теперь хеш-суммы архивов >>> get_hash_zip('archive1.zip') '0b27c443737b0a84381b827e1d9a913b' >>> get_hash_zip('archive2.zip') '0b27c443737b0a84381b827e1d9a913b' Всё чётко сработало! А что по времени? >>> timeit.timeit("get_hash_zip('archive1.zip')", number=100, globals=globals()) 10.8 Ну тоже неплохо. ⭐️ А теперь самая главная фишка ZIP - при создании архива он СРАЗУ записывает контрольную сумму файла в заголовки! А это значит что мы можем просто считать готовые хеш-суммы и сравнить их! Это называется CRC (cyclic redundancy check) def get_hash_zip2(path): h = hashlib.md5() for info in zipfile.ZipFile(path).infolist(): h.update(info.CRC.to_bytes(8, byteorder='big')) return h.hexdigest() >>> timeit.timeit("get_hash_zip2('archive1.zip')", number=100, globals=globals()) 0.008 То есть даже буфер никакой не считывается, только несколько байт из заголовков каждого файла. Выполняется моментально. В моем случае 100 итераций за 8 мс! 🌐 Полный листинг тестов в Jupyter (для экспериментов жмём Open in Colab) 📌 И просто в Gists #libs#tricks

1,720 views

Hashtags

Објавено 28 јун.

В прошлом примере мы добились совпадения хеш-суммы двух архивов. Но не даёт покоя тот факт, что делается это слишком долго. Давайте сравним скорость. >>> from timeit import timeit >>> timeit("hashlib.md5( open('archive1.tar.gz', 'rb').read() ).hexdigest()", number=100, globals=globals()) # 8.6 >>> timeit("get_hash_tar1('archive1.tar.gz')", number=100, globals=globals()) # 29.8 Разница больше чем в 3 раза! Видимо потому, что кроме простого чтения байтиков мы еще применяем алгоритм разжатия данных? Кажется что для 100 итераций это время нормальное, но представьте что архив будет размером не 50 Мб а 10Гб. Время возрастёт серьезно! Попробуем сократить разрыв. Давайте считать не все данные а только хеш файлов, который посчитает сам модуль tarfile. def get_hash_tar2(path): hsum = hashlib.md5() with tarfile.open(path) as tar: for file in tar.getmembers(): hsum.update(file.chksum.to_bytes(8, byteorder='big')) return hsum.hexdigest() >>> timeit.timeit("get_hash_tar2('archive1.tar.gz')", number=100, globals=globals()) 11.5 Прирост скорости x3! Уже неплохо, почти как просчет хеша для архива без разжатия. Почему так, можно почитать в комментарии. Если коротко, мы считываем только заголовки элементов архива. Но на сколько я понял, это не отменяет чтение всего буфера из архива. А можно быстрей? Можно... #libs#tricks

1,580 views

Hashtags

Објавено 25 јун.

Для проверки целостности или идентичности файлов всегда используется проверка контрольной суммы. Это работает в большинстве случаев, но не всегда. Давайте сделаем простой тест. Создадим несколько рандомных файлов import os # create random test files files_to_archive = [] for i in range(5): name = f'example_file{i}.txt' open(name, 'wb').write(os.urandom(10**7)) files_to_archive.append(name) Я создал 5 файлов с рандомными бинарными данными. Нам сейчас неважно что там находится, главное что это некоторые файлы по 10мб. Добавим их в архив два раза import tarfile def create_tar(archive_path, files): with tarfile.open(archive_path, 'w:gz') as tar: for file in files: tar.add(file) create_tar('archive1.tar.gz', files_to_archive) create_tar('archive2.tar.gz', files_to_archive) И проверим хеш сумму >>> hashlib.md5(open("archive1.tar.gz", "rb").read()).hexdigest() 'ded8771a6ba57281f52a0e0ec38c29b8' >>> hashlib.md5(open("archive2.tar.gz", "rb").read()).hexdigest() '2a70bd3137a174393197cf67cbe91a8d' Несмотря на то, что мы сделали два одинаковых архива, внутри он не очень-то и одинаковы! Причина тут в алгоритме сжатия, который может зависеть от некоего рандома, и в записываемых мета-данных, например время создания файла архива. Даже отличие в один байт делает хеш сумму совершенно другой, несмотря на то, что файлы внутри полностью идентичны. Чтобы решить проблему следует проверять хеш сумму самих файлов внутри архива. То есть разархивировать данные без сохранения на диск и посчитать хеш для них. def get_hash_tar(path): hsum = hashlib.md5() with tarfile.open(path) as tar: for file in tar.getmembers(): hsum.update(tar.extractfile(file).read()) return hsum.hexdigest() >>> get_hash_tar('archive1.tar.gz') '0b27c443737b0a84381b827e1d9a913b' >>> get_hash_tar('archive2.tar.gz') '0b27c443737b0a84381b827e1d9a913b' Таким образом мы обошли те байты архива которые отличаются и посчитали только фактические данные файлов. #libs#tricks

1,790 views

Hashtags

Објавено 23 јун.

Недавно писал тесты для модуля, который рисует на картинках текст и разные фигуры. Обычные ошибки в коде можно поймать простым исключением. Но как убедиться что нарисовано именно то что надо? Например цвет правильный или шрифт выбран верно. Для этого нужно визуально сравнивать правильный рендер и тест. Чтобы авто тесты оставались "авто", я использовал библиотеку imgcompare С помощью неё достаточно просто сравнить два изображения и получить процентное соотношение различий между картинками. Очень удобно проверять расхождения даже в мелочах. Например если что-то пошло не так и использовался шрифт по умолчанию. К тому же мелкие различия глазами не так уж просто заметить. Видите разницу в 1 процент на картинке к посту? Нет? А она есть🐹! ➡️https://github.com/datenhahn/imgcompare #libs#tricks

1,700 views

Hashtags

Објавено 24 мај

Чем отличается тип bytes от bytearray? Всё просто, bytes неизменяемый тип, а bytearray изменяемый. Что это нам даёт? Как известно, строка это неизменяемый тип. Всякий раз когда вы делаете любые манипуляции со строкой вы создаёте новую строку. Если же её преобразовать в bytearray то все изменения будут происходить с оригинальным объектом без копирования. Создаём массив >>> arr = bytearray(struct.pack('=11s', b'Hello World')) bytearray(b'Hello World') Можем добавить элемент в массив >>> arr.append(0) bytearray(b'Hello World\x00') Или удалить лишний элемент по индексу >>> del arr[-1] bytearray(b'Hello World') Для добавления в строку используем extend >>> arr.extend(b'!') bytearray(b'Hello World!') С помощью pack_into() вставляем данные в имеющийся массив заменяя данные >> struct.pack_into("=6s", arr, 6, b'Python') bytearray(b'Hello Python') Достаём результат >>> struct.unpack("=12s", arr)[0] b'Hello Python' И всё это мы сделали не создавая новых объектов! Это и экономит память, и выполняется быстрей, так как мы работаем с одним и тем же объектом. #tricks#libs

1,900 views

Hashtags

Објавено 21 мај

Формат структуры поддерживает две удобные фишки ▫️ Вместо дублирования токена можно указать цифру и сразу после неё нужный токен (это вы уже знаете по прошлым постам). struct.pack('=10s', data) ▫️ Для визуального удобства токены можно разделять пробелами, но не каунтеры (цифры перед токеном) struct.pack('= 10s I I 100Q', *items) #libs#tricks

1,630 views

Hashtags

Објавено 19 мај

В модуле struct есть класс Struct, специально для тех то любит в ООП. Возможно, кому-то будет удобней работать с классом вместо функций. Один раз указываем формат в конструкторе класса и получаем удобные свойства и методы. >>> st_head = struct.Struct('<20s') >>> st_head.format '<20s' >>> st_values = struct.Struct('=100i') >>> st_values.size 400 Для запаковки или распаковки просто передаём данные в соответствующие методы. >>> st_head.pack(b'some_name') b'some_name\x00\x00...' >>> st_values.pack(*range(100)) b'\x00\x00\x00\x00\x01\x00\x00...' #libs#tricks

1,790 views

Hashtags

Објавено 17 мај

Пора нам придумать свой бинарный формат😉 В качестве примера я запишу в файл анимационный канал из объекта Autodesk Maya. Можете открыть мой код↗️ и следить по тексту. Если у вас есть Maya, то можно даже запустить код и посмотреть на результат. 🔸Начнём запись! Сначала запишем имя канала, это будет имя атрибута, с которого пишется анимация. Сделаем предел в 64 байта. struct.pack('=64s', channel_name.encode()) Далее диапазон кадров, это два числа типа long struct.pack('=2L', start_frame, end_frame) Потом пишем анимацию в виде массива float значений. В примере запись идёт покадрово, то есть мы не загружаем весь массив ключей в память. for i in range(start_frame, end_frame + 1): val = obj.attr(channel_name).get(t=i) f.write(struct.pack('=f', val)) Всё, файл готов!😉 Итого у нас получился такой формат "=64s2L{N}f", где {N} это количество записанных значений. 🔸Теперь чтение. Считываем первые 64 байта, это имя канала. Первое с чем столкнёмся, это нулевые байты в имени канала, которые заполняют свободное пространство в выделенных 64 байтах. Просто удаляем их. struct.unpack('=64s', f.read(64))[0].rstrip(b'\x00') Читаем диапазон кадров, записанный в этот файл. frange_len = struct.calcsize('=2L') Функция struct.calcsize() возвращает размер данных в зависимости от указанного формата. Используем это чтобы прочитать нужное количество байт из файла. start_frame, end_frame = struct.unpack('=2L', f.read(frange_len)) Из диапазона рассчитаем длину анимации и забираем массив float значений. В коде есть вариант чтения по одному значению и полностью весь массив. key_count = end_frame - start_frame + 1 frmt = f'={key_count}f' keys = struct.unpack(frmt, f.read(struct.calcsize(frmt))) Всё, данные прочитаны!😎 Остальной код примера связан с манипуляцией объектами в Maya, чтобы визуально можно было увидеть, что анимация корректно восстановилась из файла. Вот таким образом мы придумали свой бинарный формат данных. Возможно, такие сложности вам покажутся излишними, но представьте когда данных действительно много, и один кадр содержит миллионы позиций 3D точек, и записать требуется 50000 кадров! Всё это в оперативку явно не поместится, придётся для каждого кадра делать отдельный файл. Если же мы можем писать данные постепенно, то это не проблема. Можно постепенно заполнять файл или писать несколько файлов паралельно. #libs#tricks

1,730 views

Hashtags

1234567•••10•••1415
ПретходнаСтраница 5 од 15Следна