Python 3.9 готовит нам приятный сюрприз в PEP584.
Ранее мы обсуждали как можно удобно сложить вместе два словаря получив новый словарь. Было много вариатов, но ни одного идеального. Наконец-то в Python добавили оператор для слияния словарей!!! Это оператор "|".
А это значит, что начиная с 3.9 соединить словари можно таким синтаксисом:
#pep
Ранее мы обсуждали как можно удобно сложить вместе два словаря получив новый словарь. Было много вариатов, но ни одного идеального. Наконец-то в Python добавили оператор для слияния словарей!!! Это оператор "|".
А это значит, что начиная с 3.9 соединить словари можно таким синтаксисом:
dct3 = dct1 | dct2
Чтобы обновить словарь, можно использовать такой синтаксисdct1 |= dct2
Данный функционал уже можно опробовать, установив первые релизы.#pep
Python Enhancement Proposals (PEPs)
PEP 584 – Add Union Operators To dict | peps.python.org
This PEP proposes adding merge (|) and update (|=) operators to the built-in dict class.
В Python есть стандартный модуль sched для синхронного планировщика задач. Что??? Синхронных??? В наш-то век "асинхронщины" и "параллельщины"!
Спокойно, сначала смотрим код, потом разбираемся.
Работает это так:
- создаём планировщик
- добавляем задачи в очередь с таймаутом и приоритетом
- запускаем и ждём пока завершится вся очередь
Смотрим пример:
Вывод получаем в соответствии со временем задержки:
Время указывается с момента старта очереди. Если время совпадает то сортировка идёт по приоритету.
Что значит синхронный планировщик? Это значит что задачи будут выполняться строго по очереди в одном потоке. Никаких мультипотоков и мультипроцессов, модуль прост как бревно!
Если у первой задачи стоит задержка 0сек а у второй 1сек, и при этом первая задача выполняется 3сек, то вторая задача выполнится только через 3 сек. Никакого параллельного запуска не будет.
Параметр delay следует понимать не как "запусти через N сек" а как "запусти не раньше чем через N сек". Время запуска следующей зависит от выполнения предыдущих задач.
Где это может пригодиться? Очередь задач, между которыми должен быть промежуток времени по какой-либо причине.
🔸 несколько синхронных задач, которые должны выполняться друг за другом но требующие ожидания обновления какой-либо инфраструктуры (тормозная сеть?)
🔸 сетевой API который имеет лимит на количество команд в единицу времени
🔸 фейковая задержка для генерации тестов или имитация поведения юзера.
Конечно, всё это можно решить банальным time.sleep() в нужном месте, но sched даёт несколько более удобный интерфейс управления задачами.
#libs
Спокойно, сначала смотрим код, потом разбираемся.
Работает это так:
- создаём планировщик
- добавляем задачи в очередь с таймаутом и приоритетом
- запускаем и ждём пока завершится вся очередь
Смотрим пример:
import sched, time
def func(name):
t = round(time.time()-start_time, 2)
print(f"Execute {name} ({t}s)")
# просто отметка времени старта
start_time = time.time()
# создаём планировщик
s = sched.scheduler(
time.time, # функция замера времени
time.sleep # функция ожидания
)
# добавляем задачи
s.enter(0, 1, func, argument=('ev1',))
s.enter(2, 1, func, argument=('ev2',))
s.enter(1, 1, func, argument=('ev3',))
# запускаем очередь на исполнение
s.run()
Описание аргументовВывод получаем в соответствии со временем задержки:
Execute ev1 (0.0s)
Execute ev3 (1.0s)
Execute ev2 (2.0s)
Функция run() запускает планировщик и начинается выполнение задач в порядке очереди по времени и приоритету.Время указывается с момента старта очереди. Если время совпадает то сортировка идёт по приоритету.
Что значит синхронный планировщик? Это значит что задачи будут выполняться строго по очереди в одном потоке. Никаких мультипотоков и мультипроцессов, модуль прост как бревно!
Если у первой задачи стоит задержка 0сек а у второй 1сек, и при этом первая задача выполняется 3сек, то вторая задача выполнится только через 3 сек. Никакого параллельного запуска не будет.
Параметр delay следует понимать не как "запусти через N сек" а как "запусти не раньше чем через N сек". Время запуска следующей зависит от выполнения предыдущих задач.
Где это может пригодиться? Очередь задач, между которыми должен быть промежуток времени по какой-либо причине.
🔸 несколько синхронных задач, которые должны выполняться друг за другом но требующие ожидания обновления какой-либо инфраструктуры (тормозная сеть?)
🔸 сетевой API который имеет лимит на количество команд в единицу времени
🔸 фейковая задержка для генерации тестов или имитация поведения юзера.
Конечно, всё это можно решить банальным time.sleep() в нужном месте, но sched даёт несколько более удобный интерфейс управления задачами.
#libs
В прошлом посте мы рассмотрели синхронный планировщик задач sched. Функционал вроде не плох, но синхронное выполнение с блокировкой всё портит☹️. Можем ли мы как-то поправить ситуацию?
На самом деле можем (помимо отправки всего в subprocess).
Функция run() принимает аргумент blocking, который по умолчанию True.
То есть, если мы укажем
Если мы делаем неблокирующий запуск, то после вызова метода
Получается, что вместо ожидания таймаута без полезной нагрузки планировщик освобождает поток и сообщает через сколько ему пора будет продолжить работу. А что делать с этой информацией, решаете сами.
Выполнение следующих задач произойдет после следующего вызова run() и если пришло их время выполниться.
Например, вместо ожидания следующей задачи будем делать что-то полезное:
___________________
PPS. Да, в этих примерах я нагло использую глобальные переменные))) Не делайте так на реальных проектах
На самом деле можем (помимо отправки всего в subprocess).
Функция run() принимает аргумент blocking, который по умолчанию True.
То есть, если мы укажем
blocking=False то получим неблокирующее выполнение? Нет. Этот параметр работает иначе.Если мы делаем неблокирующий запуск, то после вызова метода
run(blocking=False) планировщик выполнит все задачи, которым пришло время исполниться в обычном блокирующем синхронном режиме и вернёт время, через которое следует запуститься следующей задаче. То есть через какое время нужно запустить run(...) еще раз. Получается, что вместо ожидания таймаута без полезной нагрузки планировщик освобождает поток и сообщает через сколько ему пора будет продолжить работу. А что делать с этой информацией, решаете сами.
Выполнение следующих задач произойдет после следующего вызова run() и если пришло их время выполниться.
Например, вместо ожидания следующей задачи будем делать что-то полезное:
import sched, timeПланировщик остаётся по-прежнему синхронным, но теперь вместо бесполезного ожидания мы можем запустить другой код на исполнение и знаем когда следует вернуться к планировщику.
def func(name):
# задача
t = round(time.time()-start_time, 2)
print(f"Execute {name} ({t}s)")
start_time = time.time()
# создаём планировщик
s = sched.scheduler(time.time, time.sleep)
s.enter(0, 1, func, argument=('ev1',))
s.enter(1, 1, func, argument=('ev2',))
s.enter(2, 1, func, argument=('ev3',))
delay = 0
last_run_time = time.time()
while True:
# выполнение заданий планировщика
if last_run_time + delay < time.time():
delay = s.run(blocking=False)
if delay is None:
break
last_run_time = time.time()
# здесь делаем что-то полезное
print('Делаем что-то полезное...')
time.sleep(0.1)
print('Complete all tasks')
___________________
PPS. Да, в этих примерах я нагло использую глобальные переменные))) Не делайте так на реальных проектах
Telegram
Python Заметки
В Python есть стандартный модуль sched для синхронного планировщика задач. Что??? Синхронных??? В наш-то век "асинхронщины" и "параллельщины"!
Спокойно, сначала смотрим код, потом разбираемся.
Работает это так:
- создаём планировщик
- добавляем задачи…
Спокойно, сначала смотрим код, потом разбираемся.
Работает это так:
- создаём планировщик
- добавляем задачи…
Вы всё еще проверяете секретные данные оператором сравнения?
Чтобы сделать безопасное сравнение используйте метод secrets.compare_digest(). Он защитит операцию проверки от подобных атак.
>>> if password == user_password:Это небезопасный способ сравнения для стендалон приложений. Он уязвим к такому типу атаки как timing attack, позволяющий делать выводы и угадывать пароль на основании времени проверки.
>>> ...
Чтобы сделать безопасное сравнение используйте метод secrets.compare_digest(). Он защитит операцию проверки от подобных атак.
>>> import secretsВозможно вы ранее слышали про метод hmac.compare_digest(). Он не только делает то же самое, это один и тот же метод!
>>> if secrets.compare_digest(password, user_password):
>>> ...
>>> import secrets#libs
>>> import hmac
>>> hmac.compare_digest is secrets.compare_digest
True
Почему не стоит в коде использовать assert для проверки данных?
Действительно, команда очень удобна для быстрой проверки правдивости какого-либо факта.
Есть такая builtin константа
Стоит запустить интерпретатор в режиме оптимизации (флаг -O), константа
Действительно, команда очень удобна для быстрой проверки правдивости какого-либо факта.
assert isinstance(value, int), "Value must be type int"
Но почему советуют делать это только в тестах? Дело в том, что эта команда сделана именно для тестов и есть специальный режим когда она глобально отключается и не работает.Есть такая builtin константа
__debug__, которая по умолчанию имеет значение True (и это не изменить в коде). Именно она указывает, будут ли работать ваши assert'ы.Стоит запустить интерпретатор в режиме оптимизации (флаг -O), константа
__debug__ будет равна False, и все ваши проверки будут проигнорированы.python -O script.py
Поэтому всегда используйте raise.if not isinstance(value, int):
raise TypeError("Value must be type int")
#tricksВ стандартной поставке Python есть один полезный инструмент в библиотеке collections, это класс deque.
он очень похож на простой список но он намного быстрее работает в некоторых случаях.
Например для обработки элементов в начале списка у него есть дополнительные методы: extendleft(), appendleft() и popleft().
Запустим пару тестов!!! 🚀
Тестим функцию pop() (опустим код теста для краткости)
Почему так быстро? Дело в том что deque это некий аналог такого типа данных как "linked list data structure", в Python это занывается "двусвязные списки" (doubly-linked lists). Это список, но не в привычном представлении, а с особой оптимизированной структурой. При создании такого массива данные никуда не переносятся а только линкуются оттуда где были.
Да, это похоже на Python-лист, но линковка происходит иначе. Вместо того чтобы собирать некий стек ссылок и назвать его списком, в doubly-linked list каждый элемент просто ссылается на следующий.
Такая структура позволяет значительно ускорить, создание списка, изменение с любой стороны.
Где это может пригодиться? Конечно же в двусторонних очередях (double-ended queue). То есть когда мы добавляем элементы в начало а забираем с конца, или наоборот.
Минус такого подхода в просадке производительности для произвольного доступа к элементам в середине очереди.
Полный код тестов 🌎
______________
Реальное описание несколько сложней, я постарался передать основную суть.
#libs #tricks
он очень похож на простой список но он намного быстрее работает в некоторых случаях.
Например для обработки элементов в начале списка у него есть дополнительные методы: extendleft(), appendleft() и popleft().
Запустим пару тестов!!! 🚀
>>> from collections import dequeДобавление в конец списка работает примерно одинаково. Теперь попробуем вставлять элемент в начало массива.
>>> import time
>>>
>>> st = time.perf_counter()
>>> for _ in range(1000):
>>> l1 = deque()
>>> for i in range(5000):
>>> l1.append(i)
>>> en = round(time.perf_counter()-st, 3)
>>> print(f'Test deque: {en}sec')
>>>
>>> st = time.perf_counter()
>>> for _ in range(1000):
>>> l2 = list()
>>> for i in range(5000):
>>> l2.append(i)
>>> en = round(time.perf_counter()-st, 3)
>>> print(f'Test list: {en}sec')
Test deque: 0.452sec
Test list: 0.436sec
>>> st = time.perf_counter()Прирост производительности почти в 15 раз! 😲
>>> for _ in range(1000):
>>> l1 = deque()
>>> for i in range(5000):
>>> l1.appendleft(i)
>>> en = round(time.perf_counter()-st, 3)
>>> print(f'Test deque: {en}sec')
>>>
>>> st = time.perf_counter()
>>> for _ in range(1000):
>>> l2 = list()
>>> for i in range(5000):
>>> l2.insert(0, i)
>>> en = round(time.perf_counter()-st, 3)
>>> print(f'Test list: {en}sec')
Test deque: 0.435sec
Test list: 6.347sec
Тестим функцию pop() (опустим код теста для краткости)
Test deque: 0.48secТеперь pop(0) для list и popleft() для deque
Test list: 0.529sec
Test deque: 0.476secБыстрей примерно в 6.5 раз.
Test list: 3.101sec
Почему так быстро? Дело в том что deque это некий аналог такого типа данных как "linked list data structure", в Python это занывается "двусвязные списки" (doubly-linked lists). Это список, но не в привычном представлении, а с особой оптимизированной структурой. При создании такого массива данные никуда не переносятся а только линкуются оттуда где были.
Да, это похоже на Python-лист, но линковка происходит иначе. Вместо того чтобы собирать некий стек ссылок и назвать его списком, в doubly-linked list каждый элемент просто ссылается на следующий.
Такая структура позволяет значительно ускорить, создание списка, изменение с любой стороны.
Где это может пригодиться? Конечно же в двусторонних очередях (double-ended queue). То есть когда мы добавляем элементы в начало а забираем с конца, или наоборот.
Минус такого подхода в просадке производительности для произвольного доступа к элементам в середине очереди.
Полный код тестов 🌎
______________
Реальное описание несколько сложней, я постарался передать основную суть.
#libs #tricks
Gist
list_vs_deque.py
GitHub Gist: instantly share code, notes, and snippets.
Часто используете Python в терминале? Скорее всего вас не особо устраивает дефолтный REPL.
Советую попробовать прокаченные версии интерактивного шела:
🔸bpython
- подсветка синтаксиса
- автокомплиты
- инлайн подсказки параметров функций
- история команд
- авто отступы
Установка:
https://bpython-interpreter.org/
🔸 ptpython
- подсветка синтаксиса
- автокомплиты
- поддержка мышки
- авто отступы
- цветовые темы
Установка:
https://github.com/prompt-toolkit/ptpython
Также можно глянуть:
➡️ www.asmeurer.com/mypython
➡️ xon.sh
➡️ ipython.org
#libs
Советую попробовать прокаченные версии интерактивного шела:
🔸bpython
- подсветка синтаксиса
- автокомплиты
- инлайн подсказки параметров функций
- история команд
- авто отступы
Установка:
pip3 install bpython
Сайт 🌎https://bpython-interpreter.org/
🔸 ptpython
- подсветка синтаксиса
- автокомплиты
- поддержка мышки
- авто отступы
- цветовые темы
Установка:
pip3 install ptpython
Сайт 🌎https://github.com/prompt-toolkit/ptpython
Также можно глянуть:
➡️ www.asmeurer.com/mypython
➡️ xon.sh
➡️ ipython.org
#libs
Порой бывает необходимо работать с JSON файлами ручками, читая или изменяя данные. И очень не удобно, когда юникод в файле записан в виде кодированных символов.
Чтобы такое поведение изменить, достаточно добавить аргумент
____________________
Для тех кто в танке (всё еще на Python 2 🚂 ).
Строку следует делать как unicode, и для записи в файл использовать модуль codecs.
>>> import jsonЭх, безобразие! Ни прочитать нормально, ни поправить.
>>> data = {'title': 'Привет Медвед!'}
>>> print(json.dumps(data))
'{"title": "\\u041f\\u0440\\u0438\\u0432\\u0435\\u0442 \\u041c\\u0435\\u0434\\u0432\\u0435\\u0434!"}'
Чтобы такое поведение изменить, достаточно добавить аргумент
ensure_ascii=False
>>> json.dumps(data, ensure_ascii=False)Теперь символы не кодируются в Unicode. В файл запишется в таком же виде.
'{"title": "Привет Медвед!"}'
____________________
Для тех кто в танке (всё еще на Python 2 🚂 ).
Строку следует делать как unicode, и для записи в файл использовать модуль codecs.
>>> import json, codecs#libs #tricks
>>> data = {'title': u'Привет Медвед!'}
>>> with codecs.open(path, "w", encoding='utf-8') as f:
>>> json.dump(data, f, ensure_ascii=False)
Бывают задачи когда из большого массива объектов требуется отфильтровать эти объекты по категориям.
В результате получаем словарь примерно такого вида:
Альтернативный код делающий тоже самое:
#tricks #libs
В результате получаем словарь примерно такого вида:
data = {
"category1": [item1, item2, ...],
"category2": [item1, item2, ...],
...
}
При этом заранее мы не знаем список категорий и их необходимо добавлять в процессе итерации. Как такой код будет выглядеть:items = [...]Значением ключа является не сам элемент а промежуточный объект это список. Потому нам следует сначала убедиться, что он есть, а если нету то создать.
sorted_items = {}
for item in items:
cat = get_category(item)
if cat not in sorted_items:
sorted_items[cat] = []
sorted_items[cat].append(item)
Альтернативный код делающий тоже самое:
for item in items:Чтобы избежать этой проверки можно использовать тип defaultdict, это очень простой класс который сам за вас сделает проверку и добавит нужный тип если его нет. Просто укажите нужный тип в конструкторе.
cat = get_category(item)
if cat not in sorted_items:
sorted_items[cat] = [item]
else:
sorted_items[cat].append(item)
from collections import defauldictЕсли указанного ключа нет в словаре, то он создаётся сразу со списком в значении и возвращается как будто он там и был.
items = [...]
sorted_items = defaultdict(list)
for item in items:
sorted_items[get_category(item)].append(item)
#tricks #libs
Есть забавный трик с defaultdict, это бесконечно-рекурсивное дерево.
Самое забавное что создать его можно в одну короткую строку.
Примеры и описание на странице автора:
🌎 https://gist.github.com/hrldcpr/2012250
#tricks
Самое забавное что создать его можно в одну короткую строку.
def tree(): return defaultdict(tree)Теперь можете создавать вложенные словари любой глубины!
levels = tree()И таким образом строить любую иерархическую систему.
levels['lvl1']['lvl2']['lvl3'] = 'item'
Примеры и описание на странице автора:
🌎 https://gist.github.com/hrldcpr/2012250
#tricks
Gist
one-line tree in python
one-line tree in python. GitHub Gist: instantly share code, notes, and snippets.
В Python очень классной идеей является возможность переопределять взаимодействие объекта с любыми операторами, в том числе и унарные операторы.
И это можно очень интересно применить!
Долгое время меня смущало отсутствие возможности инкрементировать число на 1 с помощью синтаксиса С++. То есть вот так:
Давайте напишем класс, который сможет провернуть что-то подобное. А именно, сделаем чтобы величина значения увеличивалась на 1 с помощью такой записи:
Для взаимодействия с оператором "+" есть магический метод
Не буду утверждать, что пример получился полезным с точки зрения использования в реальной работе. Но для практики изучения вышло вполне интересно.
#tricks
И это можно очень интересно применить!
Долгое время меня смущало отсутствие возможности инкрементировать число на 1 с помощью синтаксиса С++. То есть вот так:
val++
Эта команда просто прибавляет единицу к значению val. В Python аналогичная операция делается так:val += 1
Ну такой cебе ZEN😭.Давайте напишем класс, который сможет провернуть что-то подобное. А именно, сделаем чтобы величина значения увеличивалась на 1 с помощью такой записи:
>>> c = Counter()
>>> print(c)
0
>>> +c
1
Да, вместо x++ мы сделаем +x, чтобы не конфликтовать со стандартным синтаксисом. Но и это уже не плохо!Для взаимодействия с оператором "+" есть магический метод
__add__, но для бинарного оператора. То есть когда операнда два. Для унарной версии оператора "+" есть метод __pos__, что означает positive. То есть как себя ведёт объект когда его пытаются сделать положительным. Вот его и используем:class Counter(object):
def __init__(self, init=0):
self.val = init
def __pos__(self, *args):
self.val += 1
def __repr__(self):
return 'Count: {}'.format(self.val)
Чтобы удобно было смотреть на результат, добавил метод __repr__. Проверяем что получится.>>> c = Counter()
>>> print(c)
Count: 0
>>> +c
Count: 1
>>> +c
Count: 2
Отлично, сделали счётчик с необычным синтаксисом! Добавим аналогичный метод для оператора "-" __neg__, что означает negate и описывает реакцию на попытку сделать число отрицательным. Теперь можем двигать значение в обе стороны:>>> class Counter(object):
...
>>> def __neg__(self):
>>> self.val -= 1
>>> c = Counter(5)
>>> print(c)
Count: 5
>>> +c
Count: 6
>>> -c
Count: 5
Стоит учесть, что данная реализация методов изменяет сам объект а не возвращает новый изменённый, как это принято в Python. Так что весь пример не очень Pythonic-way). Но такое решение реализует необходимый нам минималистичный синтаксис.Не буду утверждать, что пример получился полезным с точки зрения использования в реальной работе. Но для практики изучения вышло вполне интересно.
#tricks
Расширенный вариант каунтера из прошлого примера. Оказалось ему есть куда еще развиваться и без унарных операторов))). Добавим обработку бинарных операторов "+" и "-".
Сделаем возможность прибавлять к канутеру любое число обычным синтаксисом
Теперь такой синтаксис тоже допустим (и тоже антипаттерн!):
Еще раз о главном! Все эти операции изменяют один из операндов, что не принято в Python. Так что дальше эксперементов не стоит уходить.
Класс требует доработки чтобы не конфликтовать с принятыми нормами.
🌎 Ссылка на полный листинг
__________________________
PS. Если инкремент изменить на 0.5, то у нас получится синтаксис весьма похожий на C.
То есть, чтобы прибавить 1 надо будет написать
#tricks
Сделаем возможность прибавлять к канутеру любое число обычным синтаксисом
def __add__(self, other):Теперь можно делать так:
self.val += int(other)
def __sub__(self, other):
self.val += int(other)
>>> c = Counter(3)Добавим обработку для случая когда наш каунтер справа
>>> c + 2
>>> print(c)
Count: 5
def __radd__(self, other):Добавим во все методы возвращаемое значение и будем возвращать
self.__add__(other)
def __rsub__(self, other):
self.__sub__(other)
>>> c = Counter(3)
>>> 2 + с
>>> print(c)
Count: 5
self:def __pos__(self, *args):Тоже самое в остальных.
self.val += 1
return self
def __neg__(self):
self.val -= 1
return self
...
Теперь такой синтаксис тоже допустим (и тоже антипаттерн!):
>>> c = Counter()А так же теперь доступен такой вариант
>>> print(c)
Count: 0
>>> ++c
Count: 2
>>> ++++c
Count: 6
>>> ---c
Count: 3
>>>
c += 2
Count: 5Следующие три записи идентичны по результату
с += 2Во всех случаях каунтер с увеличится на 2
с + 2
++c
Еще раз о главном! Все эти операции изменяют один из операндов, что не принято в Python. Так что дальше эксперементов не стоит уходить.
Класс требует доработки чтобы не конфликтовать с принятыми нормами.
🌎 Ссылка на полный листинг
__________________________
PS. Если инкремент изменить на 0.5, то у нас получится синтаксис весьма похожий на C.
То есть, чтобы прибавить 1 надо будет написать
++x 😎, но это совсем уже будет вне всяких приличий 🥴#tricks
Gist
cpp-style-increment-counter.py
GitHub Gist: instantly share code, notes, and snippets.
В стандартной библиотеке os есть интересный метод os.nice().
Как написано в документации:
Add increment to the process’s "niceness".
Добавляет "любезности" процессу??? Ну почти...
В Unix системах есть стандартная утилита nice, которая может контролировать приоритет использования CPU процессом.
Обычно это значение 0, что значит стандартный приоритет. Но если добавить "любезности", то процесс будет больше отдавать другим, оставляя себе минимальный приоритет. Или наоборот.
Запустим тест чтобы визуально увидеть разницу.
🔸 Загрузите процессор на 100%.
Можно поставить любую тяжёлую задачу.
Например, сохраните этот код в файл и запустите в консоли (Python3)
🔸 Напишите функцию которая активно использует процессор и считает потраченное время.
🔸 Тестируем
🌎 Код тестов
🔸 Где может пригодиться?
- Повышение приоритета позволит захватывать больше процессорного времени, выполняя важные задачи быстрей.
- Понижение приоритета позволит повысить отзывчивость компьютера во время выполнения тяжёлых расчётов.
🔸 Что еще нужно помнить?
- Чтобы добавить приоритет нужно в функцию os.nice() отправить отрицательное значение. Так как данный метод всегда прибавляет число к текущему приоритету.
- Понижение приоритета доступно любому юзеру, повышение доступно только если процесс запущен от суперюзера.
- Узнать текущее значение:
- работает только на Linux.
- Доступный диапазон значений -20...19.
#libs
Как написано в документации:
Add increment to the process’s "niceness".
Добавляет "любезности" процессу??? Ну почти...
В Unix системах есть стандартная утилита nice, которая может контролировать приоритет использования CPU процессом.
Обычно это значение 0, что значит стандартный приоритет. Но если добавить "любезности", то процесс будет больше отдавать другим, оставляя себе минимальный приоритет. Или наоборот.
Запустим тест чтобы визуально увидеть разницу.
🔸 Загрузите процессор на 100%.
Можно поставить любую тяжёлую задачу.
Например, сохраните этот код в файл и запустите в консоли (Python3)
🔸 Напишите функцию которая активно использует процессор и считает потраченное время.
import timeМоя функция запускает 10 раз некий код и возвращает среднее время.
def compute():
array = []
for i in range(10):
start = time.perf_counter()
for i in range(1000000):
x = 2*2
array.append(time.perf_counter()-start)
return sum(array) / len(array)
🔸 Тестируем
import osВ консоль распечатается время каждого теста и разница времени.
# запускаем первый тест в дефолтным приоритетом
t1 = compute()
# меняем приоритет на минимальный
os.nice(19)
# запускаем второй тест
t2 = compute()
# смотрим реузльтат
print("Test 1", t1, "sec")
print("Test 2", t2, "sec")
print("Diff", t2/t1, "times")
🌎 Код тестов
🔸 Где может пригодиться?
- Повышение приоритета позволит захватывать больше процессорного времени, выполняя важные задачи быстрей.
- Понижение приоритета позволит повысить отзывчивость компьютера во время выполнения тяжёлых расчётов.
🔸 Что еще нужно помнить?
- Чтобы добавить приоритет нужно в функцию os.nice() отправить отрицательное значение. Так как данный метод всегда прибавляет число к текущему приоритету.
- Понижение приоритета доступно любому юзеру, повышение доступно только если процесс запущен от суперюзера.
- Узнать текущее значение:
os.nice(0)
- Запускаемые подпроцессы наследуют приоритет.- работает только на Linux.
- Доступный диапазон значений -20...19.
#libs
Как сохранить картинку непосредственно в Python-модуль?
Для этого нам пригодится библиотека base64.
Этот способ кодирование позволяет любые бинарные или текстовые данные закодировать с помощью 64 ASCII символов. То есть получится простая строка.
Зачем это вообще?
Это обратимое кодирование позволит любые бинарные данные сохранить в текстовом виде и отправить туда где для записи поддерживается только текст.
Например:
- встроить в URL в GET запрос как параметр
- встроить в тело email
- сохранить в Python-модуль как переменную
- сохранить любой конфиг, например JSON
- записать в базу данных
- зашить в HTML (XML) или CSS
Чаще всего так кодируют изображения в HTML и в CSS. Есть даже специальные сервисы для кодирование изображений.
Давайте закодируем и декодируем картинку.
Кодирование:
Декодирование:
Стоит помнить что:
- это не шифрование, пароли так не стоит прятать. Строка легко декодируется в исходник.
- размер данных после кодирования увеличивается примерно на четверть
- не храните изображения в базе данных таким способом!
#libs #tricks
Для этого нам пригодится библиотека base64.
Этот способ кодирование позволяет любые бинарные или текстовые данные закодировать с помощью 64 ASCII символов. То есть получится простая строка.
Зачем это вообще?
Это обратимое кодирование позволит любые бинарные данные сохранить в текстовом виде и отправить туда где для записи поддерживается только текст.
Например:
- встроить в URL в GET запрос как параметр
- встроить в тело email
- сохранить в Python-модуль как переменную
- сохранить любой конфиг, например JSON
- записать в базу данных
- зашить в HTML (XML) или CSS
Чаще всего так кодируют изображения в HTML и в CSS. Есть даже специальные сервисы для кодирование изображений.
Давайте закодируем и декодируем картинку.
Кодирование:
>>> import base64Теперь наша картинка это просто байты в переменной. Её можно сохранить непосредственно в модуле и использовать позже.
>>> src_path = 'image1.png'
>>> with open(src_path, 'rb') as f:
>>> raw_data = f.read()
>>> image_encoded = base64.encodebytes(raw_data)
>>> print(image_encoded)
b'iVBORw...Jggg==\n'
Декодирование:
>>> save_path = "image2.png"Картинка восстановлена обратно в файл.
>>> raw_data = base64.decodebytes(image_encoded)
>>> with open(save_path, 'wb') as f:
>>> f.write(raw_data)
Стоит помнить что:
- это не шифрование, пароли так не стоит прятать. Строка легко декодируется в исходник.
- размер данных после кодирования увеличивается примерно на четверть
- не храните изображения в базе данных таким способом!
#libs #tricks
Ещё немного про base64.
Собрал пример со встроенной в код картинкой. Это иконка для окна на PySide2. Файл кодирован в base64 и просто сохранён в переменной.
Для использования этих данных даже не пришлось сохранять их в новый файл. Иконка создаётся на лету с помощью метода QPixmap.loadFromData()
#libs #tricks #qt
Собрал пример со встроенной в код картинкой. Это иконка для окна на PySide2. Файл кодирован в base64 и просто сохранён в переменной.
Для использования этих данных даже не пришлось сохранять их в новый файл. Иконка создаётся на лету с помощью метода QPixmap.loadFromData()
...
raw_data = base64.decodebytes(ico_encoded)
ico = QPixmap()
ico.loadFromData(raw_data, "PNG")
...
🌎 Полный пример смотрите в gists.#libs #tricks #qt
Наверняка вы слышали о самом простом способе установить PIP, это скрипт get-pip.py. Скачиваем его, скармливаем интерпретатору и PIP установлен!
Если вы сомневались что данный способ хранения данных бывает полезен, то вот вам яркий пример удобного инструмента на его основе. Один простой файл в котором есть всё что ему нужно 😎
#libs
python get-pip.py
Но что это...??? Скрипт размером 1.8 МБ? Я думаю вы уже догадались почему. Смотрим докстринг в начале файла:...
This is a base85 encoding of a zip file, this zip file contains
an entire copy of pip (version ХХ.Х.Х)
...
Да, в скрипте сохранён ZIP архив! Он закодирован с помощью base85 (аналог base64) записан в переменной DATA. Если вы сомневались что данный способ хранения данных бывает полезен, то вот вам яркий пример удобного инструмента на его основе. Один простой файл в котором есть всё что ему нужно 😎
#libs
Как быстро распечатать красиво время имея в наличии число секунд ⏱?
Конечно, можно посчитать сколько в этих секундах минут, часов и потом посчитать остаток, но есть способ быстрей! Это стандартный класс
Конечно, можно посчитать сколько в этих секундах минут, часов и потом посчитать остаток, но есть способ быстрей! Это стандартный класс
datetime.timedelta
Просто создайте класс с указанием того что у вас есть и конвертните его в строку. Он всё посчитает за вас и покажет стандартный формат времени.>>> from datetime import timedelta
>>> str(timedelta(seconds=1024))
'0:17:04'
Можно просто распечатать если результат нужен в консоли>>> print(timedelta(minutes=128))
2:08:18
Также поддерживаются нецелые значения. Например, нецелое число минут будет преобразовано в секунды.>>> print(timedelta(minutes=256.5))Вот так можно распечатать полтора часа
4:16:30
>>> print(timedelta(hours=1.5))Можно выходить за пределы одних суток, появится количество дней
1:30:00
>>> print(timedelta(hours=64.32))
2 days, 16:19:12
>>> print(timedelta(weeks=20.32))
142 days, 5:45:36
А еще они поддерживают математические операции>>> print(timedelta(minutes=5) + timedelta(hours=2))#libs #tricks
2:05:00
Признавайтесь, кто хотел бы написать свой vim из которого можно легко выйти? )))
Это не так сложно как может показаться. По крайней мере движок для интерфейса уже есть в Python из коробки!
Это библиотека curses!
Для Windows она не идет в поставке. Нужно установить windows-curses
Что можно сделать? Вот несколько идей:
🖥 Крутое меню для вашего CLI
📝 Свой текстовый редактор в терминале (vim, nano)
📺 Оконный интерфейс в терминале (Norton Commander, Midnight Commander)
🐍 Игра на базе терминала (тетрис, карты, змейка)
/Каждый питонист должен написать "Змейку"! /
📊 Приложение с анимированными элементами статистики (htop)
🚀 Свой REPL c блекджеком и автокомплитами (bpython, ptpython)
__________________
🌎 Мои эксперементы за вечер, простое меню для выбора действий.
А это мануал на devdungeon.com для тех кто хочет проникнуться этой темой
#libs
Это не так сложно как может показаться. По крайней мере движок для интерфейса уже есть в Python из коробки!
Это библиотека curses!
Для Windows она не идет в поставке. Нужно установить windows-curses
pip install windows-cursesЭта библиотека превращает терминал в "ASCII canvas" и можете "рисовать" в нём с помощью любых символов что пожелаете!
Что можно сделать? Вот несколько идей:
🖥 Крутое меню для вашего CLI
📝 Свой текстовый редактор в терминале (vim, nano)
📺 Оконный интерфейс в терминале (Norton Commander, Midnight Commander)
🐍 Игра на базе терминала (тетрис, карты, змейка)
/Каждый питонист должен написать "Змейку"! /
📊 Приложение с анимированными элементами статистики (htop)
🚀 Свой REPL c блекджеком и автокомплитами (bpython, ptpython)
__________________
🌎 Мои эксперементы за вечер, простое меню для выбора действий.
А это мануал на devdungeon.com для тех кто хочет проникнуться этой темой
#libs
Для тех кто бегло просмотрел материал прошлого поста.
Вы могли не заметить ссылку на крутую библиотеку Urwid!
Это обертка для curses, реализующая интерфейс для создания оконных GUI в терминале 😳!
С этой библиотекой вы оперируете не символами и их атрибутами, а виджетами и лейаутами!
Почти как в PyQt, только для терминала.
При этом с виджетами можно взаимодействовать с помощью мыши.
Посмотрите примеры чтобы иметь представление о возможностях.
🌎 Моё меню из прошлого поста, переделанное на Urwid
Это меню понимает клики мышкой.
#libs
Вы могли не заметить ссылку на крутую библиотеку Urwid!
Это обертка для curses, реализующая интерфейс для создания оконных GUI в терминале 😳!
С этой библиотекой вы оперируете не символами и их атрибутами, а виджетами и лейаутами!
Почти как в PyQt, только для терминала.
При этом с виджетами можно взаимодействовать с помощью мыши.
Посмотрите примеры чтобы иметь представление о возможностях.
🌎 Моё меню из прошлого поста, переделанное на Urwid
Это меню понимает клики мышкой.
#libs