Функция
Но вы не сможете таким образом получить аутпут процесса который завершился с ненулевым кодом выхода. Вместо этого у вас выбрасывается исключение
Не так давно я столкнулся с этой ситуацией, когда процесс, будучи запущенным с флагом
За генерацию исключения отвечает аргумент
Нет, это не недосмотр разрабочтков и вам не потребуется искать обходные пути. Дело в том, что вся полезная нагрузка в таких случаях находится в классе исключения.
Классы TimeoutExpired и CalledProcessError имеют ряд атрибутов, которые хранят всю нужну инфу. Например, вызванная команда (
Итого, базовая фукнция для захвата аутпута для любого кода выхода будет выглядеть как-то так:
#tricks
subprocess.check_output() удобна, когда нужно просто получить аутпут процесса.info = subprocess.check_output(cmd, text=True)
Но вы не сможете таким образом получить аутпут процесса который завершился с ненулевым кодом выхода. Вместо этого у вас выбрасывается исключение
CalledProcessError: Command '[...]' returned non-zero exit status 1.
Не так давно я столкнулся с этой ситуацией, когда процесс, будучи запущенным с флагом
--help, вполне штатно печатает в аут нужную информацию но выходит с кодом 1. И это для него нормальное поведение.За генерацию исключения отвечает аргумент
check, который по умолчанию равен False но именно в check_output он равен True и не может быть переопределён при вызове.Нет, это не недосмотр разрабочтков и вам не потребуется искать обходные пути. Дело в том, что вся полезная нагрузка в таких случаях находится в классе исключения.
Классы TimeoutExpired и CalledProcessError имеют ряд атрибутов, которые хранят всю нужну инфу. Например, вызванная команда (
cmd), код выхода (returncode) и то что мы ищем - аутпут процесса (output)Итого, базовая фукнция для захвата аутпута для любого кода выхода будет выглядеть как-то так:
def get_proc_output(cmd):
try:
return subprocess.check_output(cmd, text=True)
except subprocess.CalledProcessError as e:
return e.output
#tricks
Python documentation
subprocess — Subprocess management
Source code: Lib/subprocess.py The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. This module intends to replace seve...
👍13😁3🔥1
Нередко требуется удалять дубликаты инстансов класса. Для этого обычно используется либо циклы со сравнением некоторых атрибутов, либо тип данных
При добавлении элемента в
Далее для краткости метод `__repr__()` я буду пропускать
По умолчанию в расчёте хеша, помимо прочего, используется адрес в памяти, который можно получить с помощью функции
Теперь в дело вступает логика, описаная в документации.
Если вы переопределили
Отлично, теперь всё работает.
Этот же принцип действует и при наследовании. Допустим, вы создали дочерний класс
Теперь следует учитывать вот такое поведение
Инстансы А и В могут считаться идентичными, если они имеют одинаковые значения атрибутов и хеш, что может привести к неожиданным результатам при использовании множеств. Нужно учесть это в методах:
Но если вдруг решите как-то изменить способ сравнения в классе В...
Снова получите ошибку. Та же логика - при переопределении метода
#tricks
set().При добавлении элемента в
set происходит сравнение этого объекта по хешу. Если хеш совпадает с хешем уже существующего объекта, то происходит сравнение объектов на равенство. Если объекты равны, то новый объект не добавляется.class A:
def __init__(self, pk: int):
self.pk = pk
def __repr__(self):
return f"{self.__class__.__name__}(pk={self.pk})"
set([A(pk=1), A(pk=2), A(pk=2)])
>>> {A(pk=1), A(pk=2), A(pk=2)}
Далее для краткости метод `__repr__()` я буду пропускать
По умолчанию в расчёте хеша, помимо прочего, используется адрес в памяти, который можно получить с помощью функции
id(), поэтому все объекты считаются разными. Чтобы изменить способ сравнения объектов нам требуется переопределить метод __eq__()class A:
def __init__(self, pk: int):
self.pk = pk
def __eq__(self, other):
return self.pk == other.pk
set([A(pk=1), A(pk=2), A(pk=2)])
>>> TypeError: unhashable type: 'A'
Теперь в дело вступает логика, описаная в документации.
Если вы переопределили
__eq__() то следует переопределить и __hash__().class A:
def __init__(self, pk: int):
self.pk = pk
def __eq__(self, other):
return self.pk == other.pk
def __hash__(self):
return hash(self.pk)
set([A(pk=1), A(pk=2), A(pk=2)])
>>> {A(pk=1), A(pk=2)}
Отлично, теперь всё работает.
Этот же принцип действует и при наследовании. Допустим, вы создали дочерний класс
class B(A):
pass
set([B(pk=1), B(pk=2), B(pk=2)])
>>> {B(pk=1), B(pk=2)}
Теперь следует учитывать вот такое поведение
hash(A(1)) == hash(B(1))
>>> True
set([A(1), B(1)])
>>> {A(pk=1)}
Инстансы А и В могут считаться идентичными, если они имеют одинаковые значения атрибутов и хеш, что может привести к неожиданным результатам при использовании множеств. Нужно учесть это в методах:
class A:
...
def __eq__(self, other):
return isinstance(other, self.__class__) and self.pk == other.pk
def __hash__(self):
return hash((self.pk, self.__class__))
...
Но если вдруг решите как-то изменить способ сравнения в классе В...
class B(A):
def __eq__(self, other):
return abs(self.pk) == abs(other.pk)
set([B(pk=1), B(pk=2), B(pk=2)])
>>> TypeError: unhashable type: 'B'
Снова получите ошибку. Та же логика - при переопределении метода
__eq__() в новом классе метод __hash__() автоматически становится None и его тоже требуется переопределить.#tricks
Python documentation
3. Data model
Objects, values and types: Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects. Even code is represented by objects. Ev...
👍7🔥1
Три способа создать декоратор для метода класса.
▫️Способ 1. Обычная функция.
Единственное отличие от простого декоратора функции в том, что нужно учитывать аргумент
Если же он не нужен то просто пробрасываем его через
▫️Способ 2. Методы класса.
Но что, если декоратор жестко привязан к классу и используется только в нём. И стоит задача закрепить декоратор именно за этим классом и расположить внутри него.
В таком случае можно сделать
Очевидно, что декоратор должен быть объявлен раньше метода.
Тоже самое будет и с
Где-то потерялся аргумент
▫️Способ 3. Вложенный класс и staticmethod
Получаем чтото вроде микса способов 1 и 2: функция вложена в отдельный класс.
Лучшей практикой является способ 1 - обычные функции.
Всего пару раз за практику я использовал 3й способ, когда декоратор был намертво привязан к классу и нигде больше не мог использоваться (например, отправлял вызов метода на воркера в другой процесс, не спрашивайте почему так, просто так было нужно 🤪)
Способ 2 не советую. Это, скорей, разминка для ума чем практический пример.
PS
-
- в коментах дополнительная инфа
#tricks
▫️Способ 1. Обычная функция.
Единственное отличие от простого декоратора функции в том, что нужно учитывать аргумент
self. Если же он не нужен то просто пробрасываем его через
*argsdef decorator_func(func):
def wrapped(*args, **kwargs):
print('decorator_func')
return func(*args, **kwargs)
return wrapped
class MyClass:
@decorator_func
def method(self):
print('call method')
MyClass().method()
# decorator_func
# call method
▫️Способ 2. Методы класса.
Но что, если декоратор жестко привязан к классу и используется только в нём. И стоит задача закрепить декоратор именно за этим классом и расположить внутри него.
В таком случае можно сделать
staticmethod. Это будет выглядеть страшно, но работать будет (тестировано на 3.11)Очевидно, что декоратор должен быть объявлен раньше метода.
class MyClass:
@staticmethod
def decorator(func):
def wrapper(*args, **kwargs):
print('decorator from staticmethod')
return func(*args, **kwargs)
return wrapper
@decorator.__func__
def method(self):
print('method called')
MyClass().method()
# decorator from staticmethod
# method called
Тоже самое будет и с
classmethod, но еще хуже.class MyClass:
@classmethod
def decorator(func):
def wrapper(self, *args, **kwargs):
print('decorator from classmethod')
return func(self, *args, **kwargs)
return wrapper
@decorator.__func__
def method(self):
print('method called')
MyClass().method()
# decorator from classmethod
# method called
Где-то потерялся аргумент
cls. Скорее всего это можно решить но лучше не надо. Оба варианта выглядят страшненько 🫣▫️Способ 3. Вложенный класс и staticmethod
class MyClass:
class deco:
@staticmethod
def my_decorator(func):
def wrapper(*args, **kwargs):
print('decorator from subclass')
return func(*args, **kwargs)
return wrapper
@deco.my_decorator
def method(self):
print('method called')
MyClass().method()
# decorator from subclass
# method called
Получаем чтото вроде микса способов 1 и 2: функция вложена в отдельный класс.
Лучшей практикой является способ 1 - обычные функции.
Всего пару раз за практику я использовал 3й способ, когда декоратор был намертво привязан к классу и нигде больше не мог использоваться (например, отправлял вызов метода на воркера в другой процесс, не спрашивайте почему так, просто так было нужно 🤪)
Способ 2 не советую. Это, скорей, разминка для ума чем практический пример.
PS
-
wraps пропустил для краткости- в коментах дополнительная инфа
#tricks
👍7
Объекты
Можно разделить один интервал на другой, включая целочисленное деление. Так мы узнаем сколько раз один период помещается в другой.
А так же остаток от делния.
Объекты
И, что очевидно, операторы сравнения
А еще можно почитать про форматирование даты и времени здесь и здесь.
#tricks
datetime.timedelta поддерживают операторы деления и умноженияfrom datetime import timedelta
td1 = timedelta(hours=1)
# увеличим интервал в 2.5 раза
print(td1*2.5)
# 2:30:00
# разделим интервал на 2
print(td1/2)
# 0:30:00
Можно разделить один интервал на другой, включая целочисленное деление. Так мы узнаем сколько раз один период помещается в другой.
td2 = timedelta(minutes=25)
print(td1/td2)
# 2.4
print(td1//td2)
# 2
А так же остаток от делния.
print(td1%td2)
# 0:10:00
Объекты
datetime.timedelta поддерживают отрицательные значения. Эти две записи идентичны.datetime.now() - timedelta(hours=1)
datetime.now() + timedelta(hours=-1)
И, что очевидно, операторы сравнения
td1>td2
# True
А еще можно почитать про форматирование даты и времени здесь и здесь.
#tricks
Telegram
Python Заметки
Все знают как красиво написать дату и время с помощью библиотеки datetime:
>>> from datetime import datetime
>>>
>>> dt = datetime.now()
>>> dt.strftime('%Y.%m.%d %H:%I')
'2020.01.08 12:00'
Но мало кто знает, что тоже самое можно сделать и другим способом:…
>>> from datetime import datetime
>>>
>>> dt = datetime.now()
>>> dt.strftime('%Y.%m.%d %H:%I')
'2020.01.08 12:00'
Но мало кто знает, что тоже самое можно сделать и другим способом:…
🔥15👍3
Недавно была задача форматировать строки по шаблону. Шаблон обычный для метода
или
Проблема состояла в том, что некоторые переменные следует игнорировать, заменять только те, что у меня есть на данный момент (дальше идет ещё один обработчик). Если в метод строки
Какие варианты решения есть?
▫️ переопределить класс
▫️ написать отдельную функцию парсинга строки с использованием
▫️ сделать словарь, который возвращает исходную переменную при отсутствии ключа
Третий вариант и рассмотрим, он самый краткий, буквально 2 строки включая вызов!
1. Мы создаем кастомный класс наследуя его от
2. Это не сработает если переменные указаны в формате
3. Переменные можно передать и просто готовым словарём. Это же обычный конструктор объекта
4. Вместо
5. Ну да, не две строки. Просто класс нужно создать и в одну строку. Если кому надо именно 2х-строчное решение - забирайте:
6. Из минусов: вы не сможете так просто определить все ли вам нужные переменные заполнены, так как пропускаются ВСЕ отсутствующие.
#tricks
format()/path/to/app{version}/binили
/opt/{app_name}/bin:{DEFAULT_PATH}:/usr/binПроблема состояла в том, что некоторые переменные следует игнорировать, заменять только те, что у меня есть на данный момент (дальше идет ещё один обработчик). Если в метод строки
format() не передать все переменные то будет ошибка KeyError"/opt/{app_name}:{DEFAULT_PATH}".format(app_name="my_app")
# KeyError: 'DEFAULT_PATH'Какие варианты решения есть?
▫️ переопределить класс
srt и метод format▫️ написать отдельную функцию парсинга строки с использованием
regex▫️ сделать словарь, который возвращает исходную переменную при отсутствии ключа
Третий вариант и рассмотрим, он самый краткий, буквально 2 строки включая вызов!
class SkipDict(dict):
def __missing__(self, key):
return f"{{{key}}}"
"/opt/{app_name}:{DEFAULT_PATH}".format_map(SkipDict(app_name='my_app'))
# "/opt/my_app:{DEFAULT_PATH}"
1. Мы создаем кастомный класс наследуя его от
dict и переопределяем __missing__. Этот метод вызывается когда в словаре ключ не найден. По умолчанию он выбрасывает исключение KeyError. Всякий раз когда ключ не найден, мы возвращаем исходный вид этой переменной и ошибки теперь не будет.2. Это не сработает если переменные указаны в формате
${name}. Это совсем другой синтаксис из bash и подобных сред.3. Переменные можно передать и просто готовым словарём. Это же обычный конструктор объекта
dict"...".format_map(SkipDict(kwargs))
4. Вместо
format() используется format_map(), просто удобней в данном случае.5. Ну да, не две строки. Просто класс нужно создать и в одну строку. Если кому надо именно 2х-строчное решение - забирайте:
SkipDict = type('SkipDict', (dict, ),{'__missing__': lambda self, key: f"{{{key}}}"})6. Из минусов: вы не сможете так просто определить все ли вам нужные переменные заполнены, так как пропускаются ВСЕ отсутствующие.
#tricks
👍7❤1
При использовании PNG файлов в PySide/PyQt может появляется такой ворнинг
ICC Profile — это файлы, которые содержат информацию о том, как преобразовывать цвета из одного цветового пространства в другое. Они используются для обеспечения точного цветового соответствия между различными устройствами и программами, такими как сканеры, принтеры, мониторы и графические редакторы.
sRGB (standard Red Green Blue) — стандартное цветовое пространство, которое используется в цифровой фотографии и веб-дизайне.
Указанная выше ошибка говорит о том, что профиль устарел или повреждён. Qt-движок не может его считать. Если у вас множество иконок и иных картинок оформления, то ворнингов будет сыпаться довольно много.
Решение — пересохранить файл без профиля, то есть будет использоваться цветовое пространство на усмотрение приложения.
#tricks
libpng warning: iCCP: known incorrect sRGB profile
ICC 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
👍15
POSIX (Portable Operating System Interface) — это набор стандартов, определяющих интерфейсы для обеспечения совместимости между операционными системами.
Данный стандарт поддерживается всеми UNIX-системами (GNU/Linux, macOS, FreeBSD, OpenBSD и другие). А вот в Windows либо частично, либо через подсистемы (такие как WSL).
Помимо прочих условий, один из важных моментов этого стандарта - правила синтаксического анализа строк, разбиение на токены.
В Python разбиением строки на токены занимается функция
Этот аргумент определяет, следует ли функции использовать правила синтаксического анализа соответствующие стандарту POSIX, или использовать обратно совместимый, легаси режим.
▫️posix=True
В POSIX-совместимом режиме функция
▫️posix=False
В легаси режиме используется более старый способ разбиения строк на токены, который будет игнорировать переменную окружения
Теперь смотрим некоторые примеры.
Учитвая, что аргумент
Рекомендую самостоятельно поэксперементировать с этим аргументом!
#libs
Данный стандарт поддерживается всеми 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-путями!Рекомендую самостоятельно поэксперементировать с этим аргументом!
#libs
👍7❤1
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
В июне 2022 года из-за отсутствия финансирования проект был закрыт и обновления долго не выходили. Позже и страница была удалена😭
В начале 2023 года Christoph Gohlke создал репозитории на GitHub которые заменили этот "сервис". На его странице в самом верху можно найти несколько ссылок на эти репозитории.
В частности репозиторий Pymol-open-source wheels for Python on Windows. Не могу сказать что это уже полноценная замена, кажется новые библиотеки добавляются неспеша, но это уже что-то. Активность можете проследить самостоятельно.
А еще там есть эксперементальные сборки для ARM64.
PS. Если знаете где есть подобные архивы, поделитесь в коментах.
#libs
GitHub
cgohlke - Overview
cgohlke has 63 repositories available. Follow their code on GitHub.
🔥3
Когда требуется быстро расшарить файлы в локальную сеть со своего компа можно использовать дефолтный python-сервер. Все решается одной командой.
Но это бывает неудобным если нужно скачать папку или залить файлы. В этом случае более удобным будет быстрый FTP сервер.
Я себе сделал шорткат для поднятия простого FTP сервера без авторизации на базе библиотеки pyftpdlib.
Варианты запуска:
Мой алиас для расшаривания в текущей директории
Теперь можно подключть FTP соединение как удалённую директорию стандартными средствами OS. В Windows это Add Network Location, в Linux - зависит от дистрибутива. Ищите в разделе Network вашего файлового браузера.
Также можно использовать сторонние клиенты, например FileZilla.
А здесь подробней про http.server
#libs #tricks
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 #tricks
GitHub
GitHub - giampaolo/pyftpdlib: Extremely fast and scalable Python FTP server library
Extremely fast and scalable Python FTP server library - giampaolo/pyftpdlib
🔥12👏2👍1
⭐️ Недавно состоялся релиз Python 3.13
Помимо других апдейтов, вот на что я обратил внимание:
◽️Экспериментальный компилятор JIT, должен в будущем значительно повысить скорость.
Активируется аргументом
◽️Экспериментальный режим без GIL. Думаю, все знаю что это.
Активируется аргументом
◽️Удалили ряд стандартных библиотек (PEP-594). Вместе с ними попала под нож
◽️Обновленный REPL с колоризацией.
Команда для быстрого запуска
Либо устанавливаем исталятором↗️
#release
Помимо других апдейтов, вот на что я обратил внимание:
◽️Экспериментальный компилятор JIT, должен в будущем значительно повысить скорость.
Активируется аргументом
–enable-experimental-jit◽️Экспериментальный режим без GIL. Думаю, все знаю что это.
Активируется аргументом
–without-gil◽️Удалили ряд стандартных библиотек (PEP-594). Вместе с ними попала под нож
lib2to3. Надеюсь больше никому она и не нужна)))◽️Обновленный REPL с колоризацией.
Команда для быстрого запуска
docker run --rm -it python:3.13
Либо устанавливаем исталятором↗️
#release
👍5🤔1
Регулярно приходится писать и ревьюить код, где используется PySide2-6.
Заметил, что в подавляющем большинстве случаев настройка создаваемых базовых виджетов происходит через методы. Думаю, всем знаком такой способ.
Простой пример с кнопкой:
Но есть и альтернативный способ - настройка через свойства. Это просто ключевые аргументы конструктора класса. Хоть они и не указаны в документации как аргументы, но они есть)
Этот код делает тоже самое но с помощью Property
Где это может быть полезно
▫️ Это выглядит более аккуратно и коротко, уже повод использовать
▫️ Может использоваться в заполнении лейаута, когда нам не нужно никакое другое взаимодействие с виджетом и поэтому сохранять его в переменную не требуется. Например, лейбл или кнопка.
Либо так
▫️ Можно хранить настройки в каком-то конфиге или генерировать на лету, после чего передавать как
Как получить полный список доступных свойств?
Эта функция распечатает в терминал все свойства виджета и их текущие значения
#tricks #qt
Заметил, что в подавляющем большинстве случаев настройка создаваемых базовых виджетов происходит через методы. Думаю, всем знаком такой способ.
Простой пример с кнопкой:
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.iss.onetaObject()
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 #qt
👍16🔥7
Установить свойства виджета в PySide можно не только через соответствующие методы и конструктор класса. Можно их изменять с помощью метода
Это аналогично вызову
Если указать несуществующее свойство, то оно просто создается
Получить его значение можно методом
Когда это может быть полезно?
▫️Можно просто хранить какие то данные в виджете и потом их доставать обратно
▫️ Назначая эти свойства разным виджетам можно потом отличить виджеты во время итераци по ним. Например, найти все кнопки со свойством
Да, но y
▫️ Если нам потребуется не просто поиск а, например, сортировка по числу, то свойства позволяют нам это сделать. Поддерживается любой тип данных
Да, но я думаю что не надо объяснять почему не стоит так делать. К тому же, если у виджета нет свойства то метод
▫️ Действительно полезное применение кастомным свойствам - контроль стилей. Здесь атрибутами не обойтись, нужны именно свойства.
Дело в том, что в селекторах стилей можно указывать конкретные свойства виджетов на которые следует назначать стиль.
Просто запустите этот код
С помощью селектора мы избирательно назначили стили на конкретные кнопки.
Как получить список всех кастомный свойств?
Функция получения списка кастомных свойств отличается от получения дефолтных.
#tricks #qt
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 #qt
👍1
А вот и наглядное использование контроля стилей через свойства виджета. Билиотека qt-material.
В этом разделе написано, что через свойство class можно контролировать цвет кнопки, по аналогии с css стилями.
#libs #qt
В этом разделе написано, что через свойство class можно контролировать цвет кнопки, по аналогии с css стилями.
btn_danger.setProperty('class', 'danger')
btn_warning.setProperty('class', 'warning')
btn_success.setProperty('class', 'success')#libs #qt
👍3🔥2
Что нового добавили в REPL в Python 3.13
На самом деле серьезно его прокачали!
▫️Както я писал, что для выхода из REPL приходится писать
Видимо, я не один такой😁 В новом REPL добавили несколько команд:
▫️ Автокомплит по TAB аналогичный Linux-терминалу. Одиночный TAB заполняет самое пхожее совпадение, двойной показывает все доступные варианты. Эти варианты фильтруются по мере набора.
▫️ Ранее при вставке многострочного кода с пустыми строками мы получали ошибку
Хотя, в некоторых терминалах это даже с 3.6 работает нормально, но зависит от конкретной реализации именно терминала а не Python. На винде точно не работает до 3.13.
Пример кода для теста в 3.12 и 3.13
▫️ История ввода теперь учитывает многосрочные команды.При нажатии стрелки вверх появятся все строки из прошлого многосрочного ввода, по ним даже можно перемещаться и редактировать.
По нажатию
▫️ Колоризация кода для
Кстати, для тестов на винде без установки можно использовать портейбл версию из проекта WinPython.
#release
На самом деле серьезно его прокачали!
▫️Както я писал, что для выхода из 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.
#release
Python documentation
What’s New In Python 3.13
Editors, Adam Turner and Thomas Wouters,. This article explains the new features in Python 3.13, compared to 3.12. Python 3.13 was released on October 7, 2024. For full details, see the changelog. ...
🔥7👍6
Как добавить директорию в игнор git репозитория.
1. Те, кто работает JetBrains-продуктах уже на автомате добавляют в
2. Чтобы не добавлять в каждом проекте можно добавить в глобальный игнор файл. По умолчанию он лежит здесь:
Либо указать другой путь через конфиг
Кстати, библиотека
#tricks
1. Те, кто работает JetBrains-продуктах уже на автомате добавляют в
.gitignore строчку: .idea/. Это самый простой способ.2. Чтобы не добавлять в каждом проекте можно добавить в глобальный игнор файл. По умолчанию он лежит здесь:
~/.config/git/ignore
Либо указать другой путь через конфиг
git config --global core.excludesfile ~/.gitignore
Кстати, библиотека
venv в Python 3.13 по умолчанию в корень вирутального окружения добавляет файл .gitignore с одним символом *, что означает исключение всего в текущей директории. Таким образом папка с venv автоматически исключается из репозитория. Удобно.#tricks
👍8
Доделал свой старый проект - инструмент для восстановления ориентации объектов в пространстве на основе опорных компонентов. Работает в Autdesk Maya.
Основные возможности.
▫️просто выделите опорные (обычно симметричные или осевые) компоненты и укажите куда их ориентировать
▫️ операции для доворотов и центрирования
▫️ восстановление исходного расположения после восстановления трансформаций
▫️работает как с одним объектом так и с группой
▫️ открытый API для интеграций с другими инструментами и автоматизаций
Все действия происходят с векторами и матрицами объектов, поэтому всё достаточно быстро.
Где может применяться?
🪑Нередкая проблема - собрали лейаут сцены и зафризили, или даже смержили всю геометрию в один большой меш. Требуется вернуть все объекты в "Т-позу", сохранить отдельно и расставить обратно в сцену, но с правильными трансформациями.
Инструмент как раз заточен под такую работу.
🌲Заскатерили инстансы и потом конвертнули в меш. Нужно обратно преобразовать в инстансы. Здесь поможет API который восстановит положение каждого инстанса в нуле и вернёт его обратно в исходное положение, но с правильными трансформациями. Останется забрать матрицу с объекта для инстанса.
Быстрое превью функционала:
▶️ https://www.youtube.com/watch?v=JvlHa0NEXu8
Документация и код здесь:
🌍 https://github.com/paulwinex/pw-maya-restore-orient
#release #source
Основные возможности.
▫️просто выделите опорные (обычно симметричные или осевые) компоненты и укажите куда их ориентировать
▫️ операции для доворотов и центрирования
▫️ восстановление исходного расположения после восстановления трансформаций
▫️работает как с одним объектом так и с группой
▫️ открытый API для интеграций с другими инструментами и автоматизаций
Все действия происходят с векторами и матрицами объектов, поэтому всё достаточно быстро.
Где может применяться?
🪑Нередкая проблема - собрали лейаут сцены и зафризили, или даже смержили всю геометрию в один большой меш. Требуется вернуть все объекты в "Т-позу", сохранить отдельно и расставить обратно в сцену, но с правильными трансформациями.
Инструмент как раз заточен под такую работу.
🌲Заскатерили инстансы и потом конвертнули в меш. Нужно обратно преобразовать в инстансы. Здесь поможет API который восстановит положение каждого инстанса в нуле и вернёт его обратно в исходное положение, но с правильными трансформациями. Останется забрать матрицу с объекта для инстанса.
Быстрое превью функционала:
▶️ https://www.youtube.com/watch?v=JvlHa0NEXu8
Документация и код здесь:
🌍 https://github.com/paulwinex/pw-maya-restore-orient
#release #source
YouTube
Restore Object Orient. Maya Tool.
Restore object orient tool for Autodesk Maya
Source Code: https://github.com/paulwinex/pw-maya-restore-orient
Source Code: https://github.com/paulwinex/pw-maya-restore-orient
🔥10👍5❤4
JSON API сейчас весьма актуален в сфере веб-приложений.
Но у стандартной библиотеки json есть проблема - она относительно медленная.
Если ваше веб приложение должно тянуть держать нагрузку, то такие популярные операции как сериализация и десериализация JSON хорошо бы максимально оптимизировать. Каждый запрос это преобразование JSON-строки в объект, обработка запроса, и обратное кодирование объекта в JSON-строку. Буквально - каждый!
Исходные данные для теста:
Сделаем эталонный замер стандартной библиотеки
Итак, какие есть альтернативы?
Сравнивать будем с библиотеками orjson и ujson.
Код находится на github, а здесь приведу только результаты.
Неплохой прирост с orjson я считаю! А вот ujson не особо опередил.
Тест проводился на Windows 10. Интересно, что на Linux ujson даже медленней чем стандартный. Но, думаю, это у меня какие-то проблемки.
В тесты я не добавил simplejson. Он не сильно быстрей стандартного модуля.
Если вы пишете приложения на FastAPI, то в 3 строки можете ускорить обработку JSON-ответа! Есть встроенный класс для этого.
1. Установка
2. Имопрт
3. Подключение
Для Django тоже есть решение: drf-orjson-renderer
#libs
Но у стандартной библиотеки 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
#libs
GitHub
GitHub - ijl/orjson: Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy
Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy - ijl/orjson
🔥6❤4👍2
Pyrogram закрылся!
Популярная библиотека pyrogram больше не будет развиваться. 24-го декабря мейнтейнер перевёл репозиторий в архив и написал сообщение с разъяснениями.
Если вы используете pyrogram в своих проектах, то у меня для вас плохая новость. Когда в Telegram появятся новые функции или изменится API, ваш проект перестанет работать. Рекомендую оперативно его переписывать.
Актуальная альтернатива - Telethon
➡️ https://github.com/LonamiWebs/Telethon
Документация здесь
📖 https://docs.telethon.dev/en/stable/
#libs
Популярная библиотека pyrogram больше не будет развиваться. 24-го декабря мейнтейнер перевёл репозиторий в архив и написал сообщение с разъяснениями.
Если вы используете pyrogram в своих проектах, то у меня для вас плохая новость. Когда в Telegram появятся новые функции или изменится API, ваш проект перестанет работать. Рекомендую оперативно его переписывать.
Актуальная альтернатива - Telethon
➡️ https://github.com/LonamiWebs/Telethon
Документация здесь
📖 https://docs.telethon.dev/en/stable/
#libs
😱2😢2❤1