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

TGINSIGHT CHAT

Python Заметки

@pythonotes

Education

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

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

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

Страница 5 од 32 · 384 објави

Објавено 21 окт.

Регулярно приходится писать и ревьюить код, где используется 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#qt

1,000 views

Hashtags

Објавено 18 окт.

⭐️ Недавно состоялся релиз Python 3.13 Помимо других апдейтов, вот на что я обратил внимание: ◽️Экспериментальный компилятор JIT, должен в будущем значительно повысить скорость. Активируется аргументом –enable-experimental-jit ◽️Экспериментальный режим без GIL. Думаю, все знаю что это. Активируется аргументом –without-gil ◽️Удалили ряд стандартных библиотек (PEP-594). Вместе с ними попала под нож lib2to3. Надеюсь больше никому она и не нужна))) ◽️Обновленный REPL с колоризацией. Команда для быстрого запуска docker run --rm -it python:3.13 Либо устанавливаем исталятором↗️ #release

1,240 views

Hashtags

Објавено 7 окт.

Когда требуется быстро расшарить файлы в локальную сеть со своего компа можно использовать дефолтный 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#tricks

1,470 views

Hashtags

Објавено 25 сеп.

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

1,640 views

Hashtags

Објавено 12 сеп.

⭐️⭐️⭐️ Сегодня 256-й день года, день программиста! Всех причастных — поздравляю ☕️🥸💻 #offtop

1,720 views

Hashtags

Објавено 26 авг.

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-путями! Рекомендую самостоятельно поэксперементировать с этим аргументом! #libs

1,930 views

Hashtags

Објавено 5 авг.

При использовании PNG файлов в PySide/PyQt может появляется такой ворнинг 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

1,650 views

Hashtags

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

Недавно была задача форматировать строки по шаблону. Шаблон обычный для метода 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

1,500 views

Hashtags

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

Объекты 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

1,490 views

Hashtags

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

Три способа создать декоратор для метода класса. ▫️Способ 1. Обычная функция. Единственное отличие от простого декоратора функции в том, что нужно учитывать аргумент self. Если же он не нужен то просто пробрасываем его через *args def 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

1,390 views

Hashtags

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

Нередко требуется удалять дубликаты инстансов класса. Для этого обычно используется либо циклы со сравнением некоторых атрибутов, либо тип данных 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

1,440 views

Hashtags

Функция 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

1,890 views

Hashtags

1234567•••10•••20•••303132
ПретходнаСтраница 5 од 32Следна