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

Python Заметки

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

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

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

3.00

3 отзыва

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

5 звезд

0

4 звезд

2

3 звезд

0

2 звезд

0

1 звезд

1


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

2021-05-07 12:02:24 Теперь запакуем строку.
В этом случае следует передавать тип данных bytes.

>>> struct.pack('=s', b'a')
b'a'

Для записи слова следует указывать количество символов.

>>> struct.pack('=5s', b'hello')
b'hello'

Кстати, запакованный вид соответствует исходному тексту. Всё верно, символ есть в таблице ASCII, то есть его код попадает в диапазон 0-127, он может быть записан одним байтом и имеет визуальное представление. А вот что будет если добавить символ вне ASCII

>>> struct.pack(f'=s', b'ё')
SyntaxError: bytes can only contain ASCII literal characters.

Ошибка возникла еще на этапе создания объекта bytes, который не может содержать такой символ. Поэтому надо кодировать эти байты из строки.

>>> enc = 'ёжик'.encode('utf-8')
>>> struct.pack(f'={len(enc)}s', enc)
b'\xd1\x91\xd0\xb6\xd0\xb8\xd0\xba'

Заметьте, длина такой строки в байтах отличается от исходной длины, так как символы вне ASCII записываются двумя байтами и более. Поэтому здесь формат создаём на лету, используя получившуюся длину как каунтер токена.

#libs #basic
296 views09:02
Открыть/Комментировать
2021-05-05 12:02:16 Можно ли в Python создавать бинарные файлы? Конечно можно.
Для этого в Python есть следующие инструменты:

тип данных bytes и bytearray
открытие файла в режиме wb (write binary) или rb (read binary)
модуль struct

Про модуль struct поговорим в первую очередь.
Файл в формате JSON или Yaml внутри себя содержит разметку данных. Всегда можно определить где список начался а где закончился. Где записана строка а где словарь. То есть формат записи данных содержит в себе элементы разметки данных.

В binary-файле данные не имеют визуальной разметки. Это просто байты, записанные один за другим. Правила записи и чтения находятся вне файла.

Модуль struct как раз и помогает с организацией данных в таком файле с помощью определения форматов записи для разных частей файла.
Модуль struct преобразует Python-объекты в массив байт, готовый к записи в файл и имеющий определённый вид.
Для этого всегда следует указывать формат преобразования (или, как оно здесь называется - запаковки).

Формат нужен для того, чтобы выделить достаточное количество байт для записи конкретного типа объекта. В последствии с помощью того же формата будет производиться чтение.
При этом следует помнить что мы говорим о типах языка С а не Python.
Именно формат определяет, что записано в конкретном месте файла, число, строка или что-то еще.

Вот какие токены формата у нас есть.
Помимо этого, первым символом можно указать порядок байтов. На разных системах одни и те же типы данных могут записываться по-разному, поэтому желательно указать конкретный способ из доступных. Если этого не сделать, то используется символ '@', то есть нативный для текущей системы.

В строке формата мы пишем в каком порядке и какие типы собираемся преобразовать в байты.
Запакуем в байты простое число, токен "i".

>>> import struct
>>> struct.pack('=i', 10)
b'\n\x00\x00\x00'

Теперь несколько float, при этом нужно передавать элементы не массивом а последовательностью аргументов.

>>> struct.pack('=fff', 1.0, 2.5, 4.1)
b'\x00\x00\x80?\x00\x00 @33\x83@'

Вместо нескольких токенов можно просто указать нужное количество элементов перед одним токеном, результат будет тот же.

>>> struct.pack('=3f', 1.0, 2.5, 4.1)
b'\x00\x00\x80?\x00\x00 @33\x83@'

Теперь запакуем разные типы

>>> data = struct.pack('=fiQ', 1.0, 4, 100500)

я запаковал типы float, int и unsigned long long (очень большой int, на 8 байт)

b'\x00\x00\x80?\x04\x00\x00...'

Распаковка происходит аналогично, но нужно указать тот же формат, который использовался при запаковке. Результат возвращается всегда в виде кортежа.

>>> struct.unpack('=fiQ', data)
(1.0, 4, 100500)

Как видите, ничего страшного!

#lib #basic
500 views09:02
Открыть/Комментировать
2021-05-04 12:02:12 Вышел первый бета-релиз Python 3.10

Это значит что:
- до стабильного релиза осталось примерно пол года
- ждем информацию по ветке 3.11

#offtop
727 views09:02
Открыть/Комментировать
2021-05-03 12:03:08 Один из самых удобных способов записать данные это использование готовых форматов, такие как JSON или YAML.
Из плюсов такого подхода стоит отметить вот что:

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

Но есть и минусы

затраты времени при записи файла (кодирование данных в нужный формат строки)
затраты времени при чтении файла (декодирование данных в Python объекты)
размер файла увеличивается из-за разметки данных (скобки, запятые, переносы, отступы...)
перед записью все данные должны быть помещены в память в полном объёме (не всегда)
при чтении необходимо считать весь файл в память и только потом декодировать данные

Если нужно писать немного данных в несколько файлов, то затраты по времени не ощутимы. Обычно это файлы конфига или какие-либо метаданные. Это отличный вариант под такие задачи.

Есть и другой поход к записи файлов - это бинарные файлы. Используется, когда данных достаточно много и никто их не собирается читать глазками .

очень быстрая запись
чтение значительно быстрей чем JSON, YAML итд
размер файла значительно меньше, так как нет разметки
можно записывать данные по мере поступления не загружая всё в память
можно извлечь любую часть данных независимо

Из минусов

нужно определить свой формат записи данных (если не используете готовую спецификацию определённого формата)
не получится открыть файл и визуально понять что там записано, а для чтения файла потребуется знать его спецификацию.
не так-то просто создать такой файл без специальной библиотеки

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

Представьте себе JPG-картинку. По сути это немного мета-информации и большой массив пикселей. Тоже самое со звуком или видео файлом. Поэтому, если вы попробуете открыть картинку в текстовом редакторе вы увидите что-то вроде такого

f15d cd29 a564 4578 ...
09e2 9bc4 a696 1253 ...
84e9 4de1 3b23 c24a ...
2534 5161 28e0 709d ...
...

Это и есть записанные байтики. И для их чтения требуется определённый софт который знает что с ними делать. Под каждый тип файла.

К чему это я? Читайте в следующем посте...

#tricks #basic
565 views09:03
Открыть/Комментировать
2021-04-28 12:02:22
В PyCharm есть окно со статистикой продуктивности (Help / Productivity Guide), где отображена интересная информация об использовании IDE.

В частности, можно узнать сколько нажатий на кнопки вам сэкономил автокомплит.
У меня 231К за 14 месяцев !
Не плохо конечно, но клавиатура всё равно стёрлась)))


#offtop
542 views09:02
Открыть/Комментировать
2021-04-16 12:02:04 А как вам идея сделать свойство для модуля?!
К сожалению, тут без внешних библиотек не обойтись. Но выглядит интересно!

# my_module.py
from mprop import mproperty

@mproperty
def x(mod):
print(f"Prop from '{mod.__name__}'")
return 2+2

По аналогии со свойствами класса и инстанса в функцию первым аргументом прилетает объект текущего модуля.
Теперь обращаемся к функции как к объекту модуля:

>>> import my_module
>>> my_module.x
Prop from 'my_module'
4

#tricks #libs
587 views09:02
Открыть/Комментировать
2021-04-14 12:01:37 Продолжаем со свойствами классов. Теперь мы хотим иметь рабочий setter.

Вместо класса создаем свойства для его мета-класса

class CMeta(type):
_x = 0
@property
def x(cls):
return cls._x
@x.setter
def x(cls, value):
cls._x = value

class C(metaclass=CMeta):
_x = 2

При этом можем изменить дефолтное значение для унаследованного класса. Плюс, как и в обычном property можно сделать getter, setter и deleter.

>>> C.x
2
>>> C.x = 34
>>> C.x
34

Проверим, действительно ли сработал setter или мы просто перезаписали атрибут x

>>> C._x
34

Да, всё верно!

Динамический атрибут класса. Похож на прошлый пример но с дополнительной фишкой.
Что он делает? Этот декоратор не только позволяет добавить свойство класса но и разделить функционал для свойства класса и свойства инстанса.

Работает это через дополнительный вызов __getattr__ и __setattr__ мета-класса, где требуется проверить имя атрибута и сделать соответствующие выводы.

from types import DynamicClassAttribute

class CMeta(type):
def __getattr__(self, item):
if item == 'x': # проверка имени
return 'x from class'
raise AttributeError
def __setattr__(self, key, value):
print('set class', key, '=', value)

class C(metaclass=CMeta):
@DynamicClassAttribute
def x(self):
return 'x from instance'
@x.setter
def x(self, value):
print('set instance x =', value)

>>> C.x
'from class'
>>> C().x
'from instance'
>>> C.x = 2
'set class x = 2'
C().x = 2
'set instance x = 2'

#tricks
133 views09:01
Открыть/Комментировать
2021-04-12 12:01:26 Вы используете свойства (@property) в классах?
Удобная штука, скажу я вам! Но работают они только для инстансов класса. Вот простой пример:

class A:
@property
def prop(self):
return 10

Создаём класс и получаем значение свойства

>>> a = A()
>>> a.prop
10

А что будет если вызвать свойство у класса

>>> A.prop


Эх, несвезло
Как сделать подобие property для класса?
Есть несколько способов:

Написать свой декоратор в точности повторяющий функционал property. Это ведь не сложно
Я тоже когда-то писал свою версию подобного функционала.
Примеры смотрим здесь и здесь .

Всё это здорово, но не очень-то хочется изобретать велосипед. Есть ли что-то готовое из стандартных средств?

Если вы уже перешли на Python 3.9 то можете написать вот так:

class С:
@classmethod
@property
def x(cls):
return 2+2

>>> C.x
4

Например, вот так можно динамический докстринг сделать

class C:
@classmethod
@property
def doc(cls) -> str:
return f"A doc for {cls.name!r}"

>>> help(C)
class C(builtins.object)
| A doc for 'C'
...

Хорошая новость в том, что это работает из коробки, ничего дописывать не надо.
Плохая новость — не получится сделать setter, по крайней мере лаконично и красиво.
Например, вот так не работает:

class C:
_x = 0
def x_get(cls):
return cls._x
def x_set(cls, value):
cls._x = value
x = classmethod(property(x_get, x_set))

Выражение присвоения просто перезаписывает атрибут x, а не вызывает setter.

>>> C.x = 1
>>> C._x
0

Чтобы setter тоже работал, нужно сделать иначе. Но об этом смотрите в следующем посте.

#tricks
484 views09:01
Открыть/Комментировать
2021-04-09 12:02:05
Разбираем полезные исходники!

Функция, возвращающая словарь с данными о панели задач.

screen : номер монитора
location : расположение на экране (внизу, слева и тд)
geometry : QRect с координатами таскбара
system_tray : доступен ли системный трей

Как может пригодиться?
Я использую для открытия виджета по клику на значке в трее с выравниванием по таскбару. Аналогично работает попап у Dropbox клиента.

Тестил на Windows10 и Debian10.

В комплекте проверочный виджет который при создании точно перекрывает таскбар. Проверка правильности определения геометрии таскбара. Закрывается по клику.

Ещё в комплекте пример окошка, которое появляется в районе часов над таскбаром.

Есть один баг. При перекрытии тасбара в Gnome (linux) виджет не получает событий от мыши.
Решения пока не нашел

Код забираем здесь

#source #qt
508 viewsedited  09:02
Открыть/Комментировать
2021-04-07 12:02:07 import sys
print(sys.implementation)

Выполнив этот код, вы увидите некий объект namespace с данными о текущей имплементации интерпретатора.

Помимо того что эта информация может быть как-либо полезна давайте обратим внимание на то, что это за объект вообще?
Узнаем что это за тип

>>> type(sys.implementation)


SimpleNamespace это простой тип для реализации неймспейса.

Если не знаете что такое неймспейс, то представьте себе некий объект-контейнер, куда можно складывать другие объекты. После чего обращаться к ним через имя контейнера. То есть, к имени объекта добавляется дополнительный уровень имени, что и является пространством имён.
По сути, любой модуль и класс является неймспейсом для своего содержимого (если не импортить это содержимое из модуля через "*").

Класс SimpleNamespace позволяет легко добавлять и удалять атрибуты. А также можно передать в конструктор keyword аргументы чтобы сразу создать нужные атрибуты

>>> from types import SimpleNamespace
>>> conf = SimpleNamespace(key1=1, key2=2)
>>> conf.key1
1
>>> setattr(conf, 'key3', 3)
или
>>> conf.key3 = 3
>>> conf.key3
3
>>> delattr(conf, 'key2')
или
>>> del conf.key2
>>> conf
namespace(key1=1, key3=3)

Вот так можно преобразовать словарь в неймспейс:

>>> keys_dict = {'k1': 1, 'k2': 2}
>>> keys = SimpleNamespace(**keys_dict)
>>> keys
namespace(k1=1, k2=2)

Обратное преобразование через vars

>>> vars(conf)
{'key1': 1, 'key3': 3}

Как это можно использовать?

красиво оформить "константы" или конфиг. В этом случае, кстати, я бы делал ключи аперкейсом.

>>> conf = SimpleNamespace(
**json.load(config_file.open())
)
>>> conf.API_HOST
"192.168.10.20"

быстро преобразовать словарь в подобие объекта с доступом к ключам через атрибуты. То есть вместо такой записи:

shape['width']

можно будет писать так:

shape.width

альтернатива для namedtuple, в которой доступно изменение значения атрибутов.

#tricks
638 viewsedited  09:02
Открыть/Комментировать