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Број на индексирани објави
Неодамнешен опфат26,429Збир на неодамнешни прегледи
Неодамнешни објави

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

Ознака: #tricks · 178 објави

当前筛选 #tricks清除筛选

Один из самых удобных способов записать данные это использование готовых форматов, такие как 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

1,660 views

Објавено 16 апр.

А как вам идея сделать свойство для модуля?! К сожалению, тут без внешних библиотек не обойтись. Но выглядит интересно! # 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

2,450 views

Hashtags

Објавено 14 апр.

Продолжаем со свойствами классов. Теперь мы хотим иметь рабочий 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

2,170 views

Hashtags

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

Вы используете свойства (@property) в классах? Удобная штука, скажу я вам! Но работают они только для инстансов класса. Вот простой пример: class A: @property def prop(self): return 10 Создаём класс и получаем значение свойства >>> a = A() >>> a.prop 10 А что будет если вызвать свойство у класса >>> A.prop <property object at 0x000...318> Эх, несвезло😢 Как сделать подобие 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

1,670 views

Hashtags

Објавено 7 апр.

import sys print(sys.implementation) Выполнив этот код, вы увидите некий объект namespace с данными о текущей имплементации интерпретатора. Помимо того что эта информация может быть как-либо полезна давайте обратим внимание на то, что это за объект вообще? Узнаем что это за тип >>> type(sys.implementation) <class 'types.SimpleNamespace'> 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

1,890 views

Hashtags

Објавено 22 мар.

Иногда хочется чтобы в качестве объекта передачи данных был удобный класс но не хочется (или нет возможности) писать сераиализатор в JSON для него. Идеально было бы сделать класс, который сам умел бы сериализоваться в JSON дефолтным модулем без указания дополнительных сериализаторов. Как это сделать? Стандартный модуль JSON умеет правильно сериализовать стандартные типы. Но нам нужен кастомный класс с удобными методами и свойствами. Ответ очевиден - наследуемся от стандартного типа! В качестве базового типа возьмем словарь. Например, мне нужен класс некоего абстрактного элемента списка с именем и индексом. Тогда реализация может быть такой. class Item(dict): def __init__(self, name, index=0): super().__init__(name=name, index=index) @property def name(self): return self['name'] @name.setter def name(self, value): self['name'] = value @property def index(self): return self['index'] @index.setter def index(self, value): self['index'] = value def __setitem__(self, key, value): if key not in ['index', 'name']: raise KeyError super().__setitem__(key, value) Я добавил два свойства с простым переназначением данных в словарь. Но предполагается, что там будут проверки значений и иные вычисления. Еще хорошо бы добавить методы __str__ и __repr__. >>> it = Item('item name') >>> it.name 'item name' >>> it.index 0 Также можно добавить любые методы классу. Например я переопределил __setitem__ чтобы нельзя было задать иные ключи. >>> it['key'] = 123 KeyError Хотя, можно сделать как-то иначе. К примеру, все отличные от основных ключи записывать в отдельный ключ. def __setitem__(self, key, value): if key not in ['index', 'name']: self['meta'][key] = value else: super(Item, self).setitem(key, value) Ну а главная особенность этого класса в том, что он легко понимается стандартным модулем json (конечно, если значения ключей это тоже стандартные типы или наследованные от них) >>> json.dumps(item) '{"index": 2, "name": "item name"}' Самый очевидный пример, использование в библиотеке requests. Передавая объект в аргумент json, мы не можем повлиять на его сериализацию, то есть добавить класс-сериализатор. Там ожидается готовый словарь или иной совместимый тип. import requests requests.post(url, json=item) А вот так выглядит альтернатива с методом toJson() requests.post(url, data=item.toJson(), headers={'Content-Type': 'application/json'}) А ещё ничто не мешает нам в любой момент конвертнуть наш класс в обычный словарь >>> dict(item) {"index": 2, "name": "item name"} Не спорю, возможно есть более "умные" решения этой задачи 🤓 Но, тем не менее, аналогичным образом работает стандартный collections.namedtuple. Он тоже наследуется от стандартного tuple, расширяет его функционал и сериализуется без дополнительных средств. Для более сложных случаев можно посмотреть на библиотеку dataclasses-json, которая поможет сериализовать dataclasses. #tricks#libs

2,500 views

Hashtags

Објавено 15 мар.

Кроме стандартных системных ивентов аудита можно вызывать свои ивенты. Для этого используется функция sys.audit() import sys def my_hook(event, args): if event == 'mymodule.myevent': print('Catched!', args) sys.addaudithook(my_hook) sys.audit('mymodule.myevent', 1, 2, 3) Просто указываем имя ивента первым аргументом и далее любые аргументы. Такое будет полезно если вы разрабатываете серьезную библиотеку в которой вопрос безопасности стоит не на последнем месте. Дать возможность другим разработчикам добавлять свои проверки будет хорошей практикой! Хотя никто не запрещает придумать свой способ использования перехвата подобных событий. Особенно это будет знакомо тем, кто пишет приложения на PyQt. Очень уж похоже на систему сигнал/слот в режиме DirectConnection. ▫️ следует вызывать sys.audit() ПОСЛЕ проверки данных но ДО фактического применения и исполнения логики. Это поможет избежать лишней проверки данных вне основной логики и прервать операцию в нужный момент. ▫️ в имени ивента рекомендуется использовать неймспейс с именем вашего модуля. #tricks

2,460 views

Hashtags

Објавено 12 мар.

Для Python3.8 в PEP0578 добавили функционал аудита Runtime операций. Это позволяет выполнять хуки (функции) при возникновении определённых событий в интерпретаторе. Этот функционал разработан, прежде всего, для специалистов по безопасности. Аудит позволяет перехватывать различные действия и проверять их допустимость. Полный список стандартных аудит-ивентов можно посмотреть здесь. Из названий становится ясно что мы можем перехватить. Например, мы можем перехватить факт открытия файла (open), импорта модуля (import), копирование файла (shutil.copyfile), запуск процесса (subprocess.Popen) и тд. Как минимум мы можем залогировать данное событие, как максимум, вызвать аварийное завершение программы. Примеры использования: ▫️Представим, что после разработки и долгих тестирований веб сервиса вы могли где-то оставить функцию ручного ввода данных в консоль. На продакшене такое недопустимо. С помощью аудита можно вызвать исключение перехватив ивент builtins.input ▫️С помощью ивента socket.getaddrinfo можно определить на какие сайты юзер заходил с помощью вашего приложения. ▫️Ивент exec позволит проверить загруженный объект кода перед его исполнением. Например выявить потенциально опасный код, или оставленные API ключи в строковых переменных. Как добавить свой хук? Для первого теста выполните такой код в самом начале работы приложения import sys def hook(event, args): print(f'EVENT: {event}{args}') sys.addaudithook(hook) Каждый хук вызывается для всех событий, поэтому мы можем с помощью одного хука увидеть всё что происходит в интерпретаторе. Теперь давайте посмотрим какие web-коннекты создаются при работе нашего кода. Для запросов используем requests. import sys import requests def socket_hook(event, args): if event == 'socket.getaddrinfo': print(args[0]) sys.addaudithook(socket_hook) requests.get('https://google.com') В аутпуте вы увидите домен, на который был сделан запрос. А так же: ▫️есть платформозависимые хуки (например взаимодействие с winapi) ▫️можно писать хуки на Си ▫️так как это мера для обеспечения безопасности, нет способа удалить хуки после добавления. Напоминаю, доступно в Python3.8+ #pep#tricks

2,420 views

Hashtags

Објавено 10 мар.

8 Марта🌸 вышел альфа-релиз Python 3.10.0a6 Уже сейчас можно его скачать и попробовать новый синтаксис Switch Statement, о котором я упоминал ранее. В Python его назвали Structural pattern matching Итак, как это теперь выглядит? match QUERY: case VALUE1: return 1 case VALUE2: return 2 case VALUE3: return 3 case _: # default return 0 Для объединения нескольких значений в одном кейсе используем вертикальную черту match QUERY: case VALUE1 | VALUE2: return 3 case _: # default return 0 Также можно добавлять дополнительные проверки с if match QUERY: case VALUE1: return 1 case VALUE2 | VALUE3 if x < 5: return 2 case _: # default return 0 Выглядит как синтаксический сахар для конструкции if..elif..else. В целом не плохо, но и непривычно) #tricks

2,009 views

Hashtags

Објавено 5 мар.

В Python есть удобный почтовый debug-сервер. Он поможет проверить работу почты вашего web-проекта на этапе разработки без необходимости настраивать внешние сервисы или взаимодействие с реальными серверами Google или Yandex. Этот сервер просто печатает все сообщения в консоль. Таким образом удобно дебажить одноразовые ссылки активации или просто факт отправки письма по расписанию. Запускается очень просто: python3 -m smtpd -n -c DebuggingServer localhost:1025 Теперь настройте ваш проект на использование этого сервера. Например вот так настраивается Django: # settings.py if DEBUG: EMAIL_HOST = 'localhost' EMAIL_PORT = 1025 EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False DEFAULT_FROM_EMAIL = '[email protected]' #django#tricks

2,820 views

Објавено 1 мар.

Как получить класс из модуля зная только его dotted-path? Что за dotted-path? Это путь импорта через точки вроде такого: "package.module.clsName" Так как мы не можем закинуть строку в директиву import то повторим то, что она делает только "вручную" mod = __import__('package.module', fromlist=['clsName']) cls = getattr(mod, 'clsName') Похожий способ с importlib import importlib module = importlib.import_module('package.module') cls = getattr(module, 'clsName') В обоих случаях приходится дополнительно обрабатывать строку чтобы отрезать имя класса от имени модуля. Так как мы сначала импортим модуль а потом достаём из него нужный объект. Есть ли способ просто закинуть всю строку и получить результат? Если пишете проект на Django то в нём есть функция import_string(). Она используется для импорта объектов, указанных в settings.py в виде dotted-path. from django.utils.module_loading import import_string cls = import_string('package.module.clsName') Если же у вас "чистый" Python то на помощь придёт модуль pydoc. У него тоже есть аналогичный метод для импорта объектов по dotted-path. from pydoc import locate cls = locate('package.module.clsName') В Python3.9 добавили еще один способ ресолвинга имени в объект. Теперь это самый актуальный способ на данный момент! import pkgutil cls = pkgutil.resolve_name('package.module.clsName') ____________ Конечно же это не относится только к классам. Точно так же можно импортнуть любой объект, вложенный в модуль. #tricks

2,290 views

Hashtags

Објавено 15 фев.

Как получить список всех модулей, доступных для импорта? pip list Эта команда выдаст список инсталлированных модулей, но не всех что доступны. help('modules') или python -m pydoc modules Покажет все модули которые можете импортнуть, включая те, что доступны благодаря переменной PYTHONPATH. Уже лучше, но проблема лишь в том, что функция печатает всё в аутпут, а нам нужен список строк. Конечно же в Python есть способ сделать всё просто и логично😉 import pkgutil modules = [m.name for m in pkgutil.iter_modules()] Вернёт имена всех модулей, доступных для импорта, кроме builtin модулей. Просто добавим их отдельно: import sys modules.extend(sys.builtin_module_names) Теперь мы получили полный список всех доступных для импорта модулей и пакетов, включая те, что подгружены через PYTHONPATH или динамически добавлены через sys.path. #tricks

2,090 views

Hashtags

12•••45678•••10•••1415
ПретходнаСтраница 6 од 15Следна