Получи случайную криптовалюту за регистрацию!

Python Заметки

Логотип телеграм канала @pythonotes — Python Заметки P
Логотип телеграм канала @pythonotes — Python Заметки
Адрес канала: @pythonotes
Категории: Технологии
Язык: Русский
Количество подписчиков: 2.71K
Описание канала:

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

Рейтинги и Отзывы

3.00

3 отзыва

Оценить канал pythonotes и оставить отзыв — могут только зарегестрированные пользователи. Все отзывы проходят модерацию.

5 звезд

0

4 звезд

2

3 звезд

0

2 звезд

0

1 звезд

1


Последние сообщения 5

2021-07-26 12:00:17 В стандартном модуле 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
645 views09:00
Открыть/Комментировать
2021-07-14 12:00:15 Наверняка у многих возникло желание потестить pottery, но Redis не установлен и вообще непонятно как с ним быть.
На самом деле запустить его очень просто.

Для Windows, качаем архив, запускаем redis-server.exe

Для Linux несколько команд

Если есть Docker, то еще проще

docker run --rm -p 6379:6379 redis

Всё, можно экспериментировать!
______________________
А еще с сервером можно поиграть в пинг-понг

> redis-cli ping
PONG

#tricks #libs #linux
448 viewsedited  09:00
Открыть/Комментировать
2021-07-12 12:00:11 Самый большой минус синхронизации из прошлого поста - нестабильность. Не знаю как у вас а у меня эта штука падала несколько раз)
По моему не очень production-ready.
Что же делать? Отправлять disckcache на сетевой диск? Не, я бы не стал. Ведь есть отличная альтернатива! Это Redis.

Redis это жутко быстрая in-memory база данных. Запись в неё похожа на документоориентированные NoSQL базы данных. То есть без схемы, без таблиц. Просто ключ=значение.

Redis используется для кэширования и как брокер для передачи сообщений. Имеется подписка на изменения и время жизни записей.
Вот пример кода:

import redis
R = redis.Redis()
R.set('key', 'value')
R.get('key')
# b'value'

Имея такой функционал, давайте реализуем что-то очень удобное для обмена данными по сети и для блокировок... Хотя подождите ка, всё уже придумано до нас! И это супер-проект pottery

Что он делает?
Он даёт нам некие объекты, данные которых хранятся в Redis. А сами объекты повторяют интерфейс обычных типов данных: словаря, списка, сета, очереди и тд. А так же есть RedLock (Redis Lock).

Учитывая, что действия с базой также атомарные (запросы к серверу становятся в очередь), мы гарантируем что все операции thread/process safe. Ну а если нужно сделать несколько операций, то RedLock в помощь!

Полный список возможностей здесь

Чем отличается pottery-словарь от обычного?

Данные обычного словаря доступны там, где видна переменная этого словаря. То есть локально в текущем неймспейсе или там куда вы его передаёте/импоритруете по коду.

Данные pottery-словаря доступные по сети везде, где есть доступ к тому же серверу Redis. То есть на практике выглядит так: на одном хосте записали

data['key'] = 123

а на другом прочитали

val = data['key']

Обязательное условие — наличие поднятого Redis-сервера.
Единственное ограничение, обусловленное спецификой инструментов, данные должны быть JSON serializable.
_______________
PS. Есть еще другая библиотека, ограничивающая функционал только блокировками redis-lock. Если она делает только одно, то может она делает это идеально? (я не проверял)

#libs
496 views09:00
Открыть/Комментировать
2021-07-09 12:00:25 От многопоточных вычислений переходим к распределённым. То есть вычисления, происходящие на нескольких компьютерах.
Конечно, в зависимости от задачи, вы можете взять готовые решения вроде CGRU или Deadline для рендеринга, charm4py или Dask для ML, или замутить что-то на AWS С2. Но хотелось бы чего-то попроще, попитоничней что ли)

А ведь в Python есть средства "из коробки" для синхронизации нескольких процессов на разных хостах.
Вот простой пример кода, который синхронизирует работу двух процессов на разных компьютерах.

В этом случае используется процесс-посредник, который является синхронизирующим сервером.
В примере создаётся некий Manager, который шарит общую для клиентов очередь. Все подключившиеся могут что-то в неё писать или забирать.
В моём коде один процесс что-то "считает" и складывает в очередь, другой забирает и продолжает какие-то свои "расчёты".

Если у вас есть несколько машин, то можете попробовать это запустить по сети (нужно заменить 'localhost' на IP-адрес сервера). Но и на локальной машине сработает.

Gist

#libs #source #tricks
655 viewsedited  09:00
Открыть/Комментировать
2021-07-07 12:00:14 ­Возможно, стоит пояснить разницу между синхронизацией из thread/process-safe и синхронизацией с помощью Lock

Наша задача — заставить разные процессы и потоки обращаться к базе данных (или любым другим ресурсам) последовательно. Чтобы не случилось так называемого race condition, то есть состояние гонки. Это когда разные потоки или процессы пытаются одновременно что-то сделать с одним и тем же ресурсом.
В этом случае нам нужна какая-то логика ограничения. Пока один процесс не завершил своё действие, другие не могут получить доступ к ресурсу.

Так вот, thread-safe и process-safe означает что отдельно взятые операции записи в БД гарантированно будут последовательны. Запросы из разных процессов или потоков выстроятся в очередь и не будут мешать друг другу. Лучше всего когда этот блок реализован на уровне БД в виде атомарных операций или ещё как-то.

Но зачем нам тогда еще дополнительный Lock?
Этот способ синхронизации используется когда процесс никак не укладывается в одно действие и должен сделать множество операций прежде чем дать доступ следующему. В этом случае процесс ставит некий глобальный Lock на ресурс и никто другой, даже получив законное право на доступ, не может ничего сделать. Все ждут пока этот Lock не будет снят.
Это решается на уровне приложения и правильность реализации полностью в вашей ответственности. Например, если забыли разблокировать или сделали перекрёстный Lock (Deadlock как на картинке), то всё зависнет в бесконечном ожидании.

#basic
654 views09:00
Открыть/Комментировать
2021-07-06 12:00:19
На своём авторском канале Игорь из Финляндии рассказывает про IT релокейт, как найти новую работу в Европе, прокачать LinkedIn и общаться эффективнее с зарубежными рекрутерами.

Здесь вы узнаете как произвести впечатление на рекрутера
601 views09:00
Открыть/Комментировать
2021-07-05 12:00:00 Если вы писали когда-либо мультипоточные или мультипроцессорные приложения то вы знаете какая самая большая проблема у таких программ.
Это конечно же синхронизация (мы сейчас не про GIL).

Что такое синхронизация? Это когда параллельно работающий код может делать расчёты абсолютно независимо, но вот к общим данным они должны обращаться последовательно в определённом порядке. То есть нужна синхронность вместо асинхронности.
Пока один процесс открыл файл, другие просто ждут своей очереди. Пока один поток обновляет переменную, другие просто спят, опять же ждут своей очереди.

В Python есть стандартные средства для синхронизации. Это так называемые мьютексы с различной логикой. Например Lock, Semaphore или очередь Queue. Все они работают в контексте одного интерпретатора. То есть все потоки или процессы должны быть запущены из одной программы. Тогда можно использовать один Lock между потоками и общую память между процессами.

Но что же делать, если процессы независимы? То есть я просто запускаю два разных интерпретатора с разным кодом. Возможно даже разных версий Python. В моём случае мне потребовалось писать некоторый кеш из одного модуля, который работает совершенно в разных приложениях как вспомогательный инструмент.

Сначала я пытался что-то сделать на основе shelve, но потом нашел отличную библиотеку diskcache.

Этот проект покрыл все мои потребности:

thread-safe и process-safe
То есть можно выполнять команды из разных процессов и потоков и они всегда будут синхронизированы.
Можно писать из разных процессов не опасаясь получить ошибку о том что файл занят другим процессом (как это бывает с shelve)
всегда атомарные операции
Это значит что любое действие выполняется в один запрос.
безсерверный
Не требуется отдельный процесс для синхронизации. Всё решается через базу данных.

Полный список возможностей

Из удобных фичей можно еще отметить встроенный Lock, позволяющий синхронизировать независимые процессы. Это как раз то что я искал!

На что стоит обратить внимание:
несовместимы версии python 2 и 3 из-за разницы протоколов pickle
надёжность работы с сетевыми дисками под вопросом, я бы не стал. Но тут скорей вопрос к сети чем к софту.
при создании инстанса diskcache.Cache() все данные пишутся в рандомную директорию в temp. Чтобы синхронизировать разные процессы следует указывать одинаковый путь diskcache.Cache(some_path).

#libs
474 views09:00
Открыть/Комментировать
2021-07-02 12:00:23 Подводя итоги по прошлым постам плюс пара заметок:

Если требуется проверять идентичность содержимого архивов, лучше использовать ZIP и проверять только CRC.

Указанный способ может помочь проверить отдельные файлы в архиве с возможностью перекачать только новые а не весь архив

Для проверки хеш-суммы файла можно использовать утилиту md5sum (она не умеет проверять хеш внутри ахрхивов)

# Linux:
md5sum filename

# Windows:
python md5sum.py filename

(находится в директории скриптов /Tools/scripts/md5sum.py)

#libs #tricks
526 views09:00
Открыть/Комментировать
2021-06-30 12:00:19 На самом деле архивы 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
404 views09:00
Открыть/Комментировать
2021-06-29 10:01:06
Хочешь повысить свой скилл в программировании на Python? У меня есть 3 способа!

Дурацкий способ. Скачать кучу книг для чайников, отложить их до лучших времен и никогда не читать.

Сложный. Открыть IDE, создать проект, бесконечно долго гуглить, тысячу раз отчаяться, не сдаться и придти к Дзену. Займёт 3-4 года.

Простой. Подписаться на Python nation, где ты найдешь много интересных постов которые взорвут твой скилл и расширят возможности программирования на Python.

Один канал вместо 1000 учебников и бесполезных туториалов на Ютубе — @pythonnation
677 views07:01
Открыть/Комментировать