Python Заметки
الذهاب إلى القناة على Telegram
Интересные заметки и обучающие материалы по Python Контакт: @paulwinex ⚠️ Рекламу на канале не делаю!⚠️ Хештеги для поиска: #tricks #libs #pep #basic #regex #qt #django #2to3 #source #offtop
إظهار المزيد2 373
المشتركون
لا توجد بيانات24 ساعات
-17 أيام
-1130 أيام
أرشيف المشاركات
2 373
В Python 3.14 появится реализация PEP 750 и новый способ форматирования: t-strings. Это так называемые Template Strings.
Синтаксис такой же как с f-strings, но форматирование происходит не сразу.
Вместо строки создаётся объект Template, который внутри себя содержит исходную информацию, сырую строку (
template.strings) и переменные (template.values).
Это позволяет произвести дополнительную обработку данных перед форматированием, например для усиления безопасности.
В примерах можно увидеть как строка с HTML кодом дополнтиельно обрабатывается чтобы избежать инъекции JS кода за счет экранирования служебных символов.
Конечно, этим примером возможности не ограничивюатся. Более подробно про функционал будет понятно ближе к релизу в конце года. Сейчас доступно в сборках 3.14.0a7+ из этой ветки.
Простой пример создания шаблона
name = "World"
template = t"Hello {name}!"
Что является шорткатом для
from string.templatelib import Template, Interpolation
template = Template(
"Hello ",
Interpolation(value="World", expression="name"),
"!"
)
В обоих случаях объект получим идентичный
print(isinstance(template, Template))
# True
print(template.strings)
# ("Hello ", "!")
print(template.values)
#(name,)
Больше примеров ➡️ здесь
#pep2 373
Небольшая вспомогательная функция для запуска процессов.
Что умеет:
▫️запускать сабпроцесс в обычном режиме с перехватом текущего терминала
▫️запускать процесс без терминала
▫️запускать детаченый процесс (никак не привязан к текущем процессу)
▫️запускать в отдельном терминале как дочерний с блокировкой, без блокировки и детаченый.
▫️передавать переменные окружения для нового процесса
▫️перенаправлять аутпут детаченого процесса в файл (полезно если он без терминала)
Забираем здесь ↗️
#sources
2 373
Pyrogram закрылся!
Популярная библиотека pyrogram больше не будет развиваться. 24-го декабря мейнтейнер перевёл репозиторий в архив и написал сообщение с разъяснениями.
Если вы используете pyrogram в своих проектах, то у меня для вас плохая новость. Когда в Telegram появятся новые функции или изменится API, ваш проект перестанет работать. Рекомендую оперативно его переписывать.
Актуальная альтернатива - Telethon
➡️ https://github.com/LonamiWebs/Telethon
Документация здесь
📖 https://docs.telethon.dev/en/stable/
#libs
2 373
JSON API сейчас весьма актуален в сфере веб-приложений.
Но у стандартной библиотеки json есть проблема - она относительно медленная.
Если ваше веб приложение должно тянуть держать нагрузку, то такие популярные операции как сериализация и десериализация JSON хорошо бы максимально оптимизировать. Каждый запрос это преобразование JSON-строки в объект, обработка запроса, и обратное кодирование объекта в JSON-строку. Буквально - каждый!
Исходные данные для теста:
file = 'data.json' # 156kb
with open(file, 'r') as f:
file_data = f.read()
data = json.loads(file_data)
count = 50000
Сделаем эталонный замер стандартной библиотеки
start = time.perf_counter()
for i in range(count):
json.loads(file_data)
end = time.perf_counter()
json_time_decode = end - start
start = time.perf_counter()
for i in range(count):
json.dumps(data)
end = time.perf_counter()
json_time_encode = end - start
print(f'Json time: {round(json_time_decode, 2)}s/{round(json_time_encode)}s')
# Json time: 50.04s/40s
Итак, какие есть альтернативы?
Сравнивать будем с библиотеками orjson и ujson.
Код находится на github, а здесь приведу только результаты.
Json time: 50.04s/40s Orjson time: 20.27s [2.47x speed] / 5.46s [7.35x speed] Ujson time: 47.29s [1.06x speed] / 35.04s [1.15x speed]Неплохой прирост с orjson я считаю! А вот ujson не особо опередил. Тест проводился на Windows 10. Интересно, что на Linux ujson даже медленней чем стандартный. Но, думаю, это у меня какие-то проблемки. В тесты я не добавил simplejson. Он не сильно быстрей стандартного модуля. Если вы пишете приложения на FastAPI, то в 3 строки можете ускорить обработку JSON-ответа! Есть встроенный класс для этого. 1. Установка
poetry add orjson
# or
pip install orjson
2. Имопрт
from fastapi.responses import ORJSONResponse
# Там же лежит и UJSONResponse
3. Подключение
app = FastAPI(
...
default_response_class=ORJSONResponse
)
Для Django тоже есть решение: drf-orjson-renderer
#libs2 373
Доделал свой старый проект - инструмент для восстановления ориентации объектов в пространстве на основе опорных компонентов. Работает в Autdesk Maya.
Основные возможности.
▫️просто выделите опорные (обычно симметричные или осевые) компоненты и укажите куда их ориентировать
▫️ операции для доворотов и центрирования
▫️ восстановление исходного расположения после восстановления трансформаций
▫️работает как с одним объектом так и с группой
▫️ открытый API для интеграций с другими инструментами и автоматизаций
Все действия происходят с векторами и матрицами объектов, поэтому всё достаточно быстро.
Где может применяться?
🪑Нередкая проблема - собрали лейаут сцены и зафризили, или даже смержили всю геометрию в один большой меш. Требуется вернуть все объекты в "Т-позу", сохранить отдельно и расставить обратно в сцену, но с правильными трансформациями.
Инструмент как раз заточен под такую работу.
🌲Заскатерили инстансы и потом конвертнули в меш. Нужно обратно преобразовать в инстансы. Здесь поможет API который восстановит положение каждого инстанса в нуле и вернёт его обратно в исходное положение, но с правильными трансформациями. Останется забрать матрицу с объекта для инстанса.
Быстрое превью функционала:
▶️ https://www.youtube.com/watch?v=JvlHa0NEXu8
Документация и код здесь:
🌍 https://github.com/paulwinex/pw-maya-restore-orient
#release #source
2 373
⭐️ Всем привет. Я завершил очередной проект!
Он еще не достаточно причёсан чтобы добавлять в портфель, но как только, так сразу добавлю в галерею.
А пока я открыт к предложениям по работе. Фултайм удалёнка.
Вся инфа здесь https://paulwinex.com/resume
Контакт: @paulwinex
2 373
Как добавить директорию в игнор git репозитория.
1. Те, кто работает JetBrains-продуктах уже на автомате добавляют в
.gitignore строчку: .idea/. Это самый простой способ.
2. Чтобы не добавлять в каждом проекте можно добавить в глобальный игнор файл. По умолчанию он лежит здесь:
~/.config/git/ignoreЛибо указать другой путь через конфиг
git config --global core.excludesfile ~/.gitignore
Кстати, библиотека venv в Python 3.13 по умолчанию в корень вирутального окружения добавляет файл .gitignore с одним символом *, что означает исключение всего в текущей директории. Таким образом папка с venv автоматически исключается из репозитория. Удобно.
#tricks2 373
Что нового добавили в REPL в Python 3.13
На самом деле серьезно его прокачали!
▫️Както я писал, что для выхода из REPL приходится писать
exit, еще и скобки для вызова. И было бы удобней сделать это по аналогии с обычным терминалом.
Видимо, я не один такой😁 В новом REPL добавили несколько команд:
exit или quit: для выхода. Именно так, без вызова функции!
clear: для очистки терминала
help или F1: для входа в режим справки (q для выхода)
▫️ Автокомплит по TAB аналогичный Linux-терминалу. Одиночный TAB заполняет самое пхожее совпадение, двойной показывает все доступные варианты. Эти варианты фильтруются по мере набора.
▫️ Ранее при вставке многострочного кода с пустыми строками мы получали ошибку IndentationError, теперь это исправили.
Хотя, в некоторых терминалах это даже с 3.6 работает нормально, но зависит от конкретной реализации именно терминала а не Python. На винде точно не работает до 3.13.
Пример кода для теста в 3.12 и 3.13
class A:
def test(self):
pass
▫️ История ввода теперь учитывает многосрочные команды.При нажатии стрелки вверх появятся все строки из прошлого многосрочного ввода, по ним даже можно перемещаться и редактировать.
По нажатию F2 можно открыть всю историю ввода.
▫️ Колоризация кода для tracebacks и doctest. Также я заметил что имеет цвет промт функции input().
Кстати, для тестов на винде без установки можно использовать портейбл версию из проекта WinPython.
#release2 373
А вот и наглядное использование контроля стилей через свойства виджета. Билиотека qt-material.
В этом разделе написано, что через свойство class можно контролировать цвет кнопки, по аналогии с css стилями.
btn_danger.setProperty('class', 'danger')
btn_warning.setProperty('class', 'warning')
btn_success.setProperty('class', 'success')
#libs #qt2 373
Установить свойства виджета в PySide можно не только через соответствующие методы и конструктор класса. Можно их изменять с помощью метода
setProperty по имени.
btn = QPushButton("Click Me")
btn.setProperty("flat", True)
Это аналогично вызову
btn.setFlat(True)
Если указать несуществующее свойство, то оно просто создается
btn.setProperty("btnType", "super")
Получить его значение можно методом .property(name)
btn_type = btn.property("btnType")
Когда это может быть полезно?
▫️Можно просто хранить какие то данные в виджете и потом их доставать обратно
widget = QWidget()
widget.setProperty('my_data', 123)
print(widget.property('my_data'))
▫️ Назначая эти свойства разным виджетам можно потом отличить виджеты во время итераци по ним. Например, найти все кнопки со свойством my_data="superbtn".
Но ведь вместо кастомного свойства можно использовать objectName, будет тот же результат.Да, но y
ObjectName есть ограничение - только строки.
▫️ Если нам потребуется не просто поиск а, например, сортировка по числу, то свойства позволяют нам это сделать. Поддерживается любой тип данных
widget.setProperty('my_data', {'Key': 'value'})
widget.setProperty('order', 1)
all_widgets.sort(key=w: w.property('order'))
Но ведь Python позволяет всё вышеперечисленное сделать простым созданием атрибута у объекта widget.order = 1 widget.my_data = 123Да, но я думаю что не надо объяснять почему не стоит так делать. К тому же, если у виджета нет свойства то метод
.property(name) вернет None, а отсутствующий атрибут выбросит исключение.
▫️ Действительно полезное применение кастомным свойствам - контроль стилей. Здесь атрибутами не обойтись, нужны именно свойства.
Дело в том, что в селекторах стилей можно указывать конкретные свойства виджетов на которые следует назначать стиль.
Просто запустите этот код
from PySide2.QtWidgets import *
if __name__ == "__main__":
app = QApplication([])
widget = QWidget(minimumWidth=300)
layout = QVBoxLayout(widget)
btn1 = QPushButton("Action 1")
btn2 = QPushButton("Action 2")
btn3 = QPushButton("Action 3", flat=True)
layout.addWidget(btn1)
layout.addWidget(btn2)
layout.addWidget(btn3)
# добавим кастомное свойство одной кнопке
btn1.setProperty("btnType", "super")
# добавляем стили
widget.setStyleSheet(
"""
QPushButton[btnType="super"] {
background-color: yellow;
color: red;
}
QPushButton[flat="true"] {
color: yellow;
}
"""
)
widget.show()
app.exec_()
С помощью селектора мы избирательно назначили стили на конкретные кнопки.
Как получить список всех кастомный свойств?
Функция получения списка кастомных свойств отличается от получения дефолтных.
def print_widget_dyn_properties(widget):
for prop_name in widget.dynamicPropertyNames():
property_name = prop_name.data().decode()
property_value = widget.property(property_name)
print(f"{property_name}: {property_value}")
#tricks #qt2 373
Регулярно приходится писать и ревьюить код, где используется PySide2-6.
Заметил, что в подавляющем большинстве случаев настройка создаваемых базовых виджетов происходит через методы. Думаю, всем знаком такой способ.
Простой пример с кнопкой:
button = QPushButton("Click Me")
button.setMinimumWidth(300)
button.setFlat(True)
button.setStyleSheet("font-size: 20pt")
button.setToolTip("Super Button")
button.clicked.connect(lambda: print("Button clicked"))
Но есть и альтернативный способ - настройка через свойства. Это просто ключевые аргументы конструктора класса. Хоть они и не указаны в документации как аргументы, но они есть)
Этот код делает тоже самое но с помощью Property
button = QPushButton(
"Click Me",
minimumWidth=300,
flat=True,
styleSheet="font-size: 20pt",
toolTip="Super Button",
clicked=lambda: print("Button clicked"),
)
Где это может быть полезно
▫️ Это выглядит более аккуратно и коротко, уже повод использовать
▫️ Может использоваться в заполнении лейаута, когда нам не нужно никакое другое взаимодействие с виджетом и поэтому сохранять его в переменную не требуется. Например, лейбл или кнопка.
widget = QWidget(minimumWidth=400)
layout = QHBoxLayout(widget)
layout.addWidget(QLabel("Button >", alignment=Qt.AlignRight))
layout.addWidget(QPushButton("Click Me", clicked=lambda: print("Button clicked")))
widget.show()
Либо так
widget = QWidget(minimumWidth=400)
layout = QHBoxLayout(widget)
for wd in (
QLabel("Button >", alignment=Qt.AlignRight),
QPushButton("Click Me", clicked=lambda: ...)
):
layout.addWidget(wd)
widget.show()
▫️ Можно хранить настройки в каком-то конфиге или генерировать на лету, после чего передавать как kwargs.
kwargs = {"text": "Hello " * 30, "wordWrap": True}
my_label = QLabel(**kwargs)
Как получить полный список доступных свойств?
Эта функция распечатает в терминал все свойства виджета и их текущие значения
def print_widget_properties(widget):
meta_object = widget.metaObject()
for i in range(meta_object.propertyCount()):
property_ = meta_object.property(i)
property_name = property_.name()
property_value = property_.read(widget)
print(f"{property_name}: {property_value}")
#tricks #qt2 373
⭐️ Недавно состоялся релиз Python 3.13
Помимо других апдейтов, вот на что я обратил внимание:
◽️Экспериментальный компилятор JIT, должен в будущем значительно повысить скорость.
Активируется аргументом
–enable-experimental-jit
◽️Экспериментальный режим без GIL. Думаю, все знаю что это.
Активируется аргументом –without-gil
◽️Удалили ряд стандартных библиотек (PEP-594). Вместе с ними попала под нож lib2to3. Надеюсь больше никому она и не нужна)))
◽️Обновленный REPL с колоризацией.
Команда для быстрого запуска
docker run --rm -it python:3.13
Либо устанавливаем исталятором↗️
#release2 373
Когда требуется быстро расшарить файлы в локальную сеть со своего компа можно использовать дефолтный python-сервер. Все решается одной командой.
python3 -m http.server
Но это бывает неудобным если нужно скачать папку или залить файлы. В этом случае более удобным будет быстрый FTP сервер.
Я себе сделал шорткат для поднятия простого FTP сервера без авторизации на базе библиотеки pyftpdlib.
Варианты запуска:
# на рандомном порту read only
python3 -m pyftpdlib
# на указанном порту
python3 -m pyftpdlib -p 22222
# с доступом на запись
python3 -m pyftpdlib -w
# с авторизацией
python3 -m pyftpdlib -w --user=name --password=123
# полный список аргументолв
python3 -m pyftpdlib -h
Мой алиас для расшаривания в текущей директории
alias ftp="python3 -m pyftpdlib -w -p 22222"
Теперь можно подключть FTP соединение как удалённую директорию стандартными средствами OS. В Windows это Add Network Location, в Linux - зависит от дистрибутива. Ищите в разделе Network вашего файлового браузера.
Также можно использовать сторонние клиенты, например FileZilla.
А здесь подробней про http.server
#libs #tricks2 373
Unofficial Windows Binaries for Python Extension Packages - известная страница с множеством скомпилированных python-библиотек для Windows. Её вёл Christoph Gohlke и любезно нам собирал whl пакеты. Очень часто эа страница помогала и мне и, вероятно, многим из вас.
В июне 2022 года из-за отсутствия финансирования проект был закрыт и обновления долго не выходили. Позже и страница была удалена😭
В начале 2023 года Christoph Gohlke создал репозитории на GitHub которые заменили этот "сервис". На его странице в самом верху можно найти несколько ссылок на эти репозитории.
В частности репозиторий Pymol-open-source wheels for Python on Windows. Не могу сказать что это уже полноценная замена, кажется новые библиотеки добавляются неспеша, но это уже что-то. Активность можете проследить самостоятельно.
А еще там есть эксперементальные сборки для ARM64.
PS. Если знаете где есть подобные архивы, поделитесь в коментах.
#libs
2 373
⭐️⭐️⭐️ Сегодня 256-й день года, день программиста!
Всех причастных — поздравляю ☕️🥸💻
#offtop
2 373
POSIX (Portable Operating System Interface) — это набор стандартов, определяющих интерфейсы для обеспечения совместимости между операционными системами.
Данный стандарт поддерживается всеми UNIX-системами (GNU/Linux, macOS, FreeBSD, OpenBSD и другие). А вот в Windows либо частично, либо через подсистемы (такие как WSL).
Помимо прочих условий, один из важных моментов этого стандарта - правила синтаксического анализа строк, разбиение на токены.
В Python разбиением строки на токены занимается функция
shlex.split(), которая имеет один важный аргумент - posix.
Этот аргумент определяет, следует ли функции использовать правила синтаксического анализа соответствующие стандарту POSIX, или использовать обратно совместимый, легаси режим.
▫️posix=True
В POSIX-совместимом режиме функция shlex.split() будет учитывать переменную окружения IFS (Internal Field Separator) для определения разделителей полей и будет более строго следовать стандарту POSIX. Из строки удаляются неэкранированные кавычки и обратные слеши.
▫️posix=False
В легаси режиме используется более старый способ разбиения строк на токены, который будет игнорировать переменную окружения IFS и использовать whitespaces как разделители полей.
Теперь смотрим некоторые примеры.
import shlex
# кавычки
text = r'"Do"Not"Separate" \"This\"'
shlex.split(text, posix=False)
# ['"Do"', 'Not"Separate"', '\\"This\\"']
shlex.split(text, posix=True)
# ['DoNotSeparate', '"This"']
# специсимволы
text = r'A\tB\nС\fD\vE'
shlex.split(text, posix=False)
# ['A\\tB\\nС\\fD\\vE']
shlex.split(text, posix=True)
# ['AtBnСfDvE']
# обратный слеш
text = r"cmd.exe c:\games\mario.exe"
shlex.split(text, posix=False)
# ['cmd.exe', 'c:\\games\\mario.exe']
shlex.split(text, posix=True)
# ['cmd.exe', 'c:gamesmario.exe']
Учитвая, что аргумент posix по умолчанию True, стоит помнить этот факт при обработке строк с Windows-путями!
Рекомендую самостоятельно поэксперементировать с этим аргументом!
#libs2 373
При использовании PNG файлов в PySide/PyQt может появляется такой ворнинг
libpng warning: iCCP: known incorrect sRGB profileICC Profile — это файлы, которые содержат информацию о том, как преобразовывать цвета из одного цветового пространства в другое. Они используются для обеспечения точного цветового соответствия между различными устройствами и программами, такими как сканеры, принтеры, мониторы и графические редакторы. sRGB (standard Red Green Blue) — стандартное цветовое пространство, которое используется в цифровой фотографии и веб-дизайне. Указанная выше ошибка говорит о том, что профиль устарел или повреждён. Qt-движок не может его считать. Если у вас множество иконок и иных картинок оформления, то ворнингов будет сыпаться довольно много. Решение — пересохранить файл без профиля, то есть будет использоваться цветовое пространство на усмотрение приложения.
from PIL import Image
Image.open(input_path).save(output_path, icc_profile=None)
from PySide2.QtGui import QImage, QImageWriter
image = QImage(input_path)
image.setText("icc", "")
writer = QImageWriter(output_path)
writer.write(image)
#tricks