TGINSIGHT CHAT
Python Заметки
@pythonotes
EducationИнтересные заметки и обучающие материалы по Python Контакт: @paulwinex ⚠️ Рекламу на канале не делаю!⚠️ Хештеги для поиска: #tricks #libs #pep #basic #regex #qt #django #2to3 #source #offtop
Неодамнешни објави
Ознака: #libs · 124 објави
Објавено 11 дек.
Еще раз про JSON Для создания кастомной сериализации объектов в JSON не обязательно создавать класс-сериализатор. Достаточно указать функцию default() которую в свою очередь подать в виде лямбды. За читаемость примера не ручаюсь но выйдет что-то вроде такого: json.dumps(my_data, default=lambda obj: { 'type': f'{obj.__class__.__module__}.{obj.__class__.__name__}', 'data': getattr(obj, '__dict__', repr(obj))}) В примере я добавил полный путь к классу включая имя модуля. ⚠️ Повторяю! В ситуации, когда данные неизвестны, такой подход может привести к непредсказуемому поведению! Лично я использовал его только для дебага, когда требовалось получить хоть что-то в виде JOSN а не ошибку. Явное лучше чем неявное 😉 Кстати, обычная функция часто лучше чем лямбда 😬 Так что вам эта же функция в нормальном виде def default_hook(obj): return { 'type': f'{obj.__class__.__module__}.' f'{obj.__class__.__name__}', 'data': getattr(obj, '__dict__', repr(obj)) } json.dumps(my_data, default=default_hook) Почему бы не организовать поддержку всех стандартных объектов Python в стандартном JSON-энкодере? Я думаю дело в неочевидности этого процесса. Как можно сериализовать float? Тут вполне очевидно. А как сериализовать datetime? Вот тут тысяча и один вариант как можно форматировать дату. Поэтому данный этап отадётся на откуп разработчику. Меня устроило бы добавление в спецификацию класса метода __json__ по аналогии с __fspath__, который использовался бы стандартным энкодером. Метод возвращал бы поддерживаемый для JSON объект. Тогда не требуется что-то указывать в функции dump() и наш класс может использоваться в других модулях, где код уже записан и вставить что-то в default м не можем. Но пока этого нет (и будет ли?) мы по-прежнему добавляем в класс метод toJson() и вызываем его, отправляя в json.dump(). #tricks#libs
Објавено 9 дек.
В прошлом посте мы научили JSON понимать новый тип данных. Но что если придётся записывать в JSON много разных неподдерживаемых типов? Описывать для каждого отдельный if isinstance()? А если нам не известно что именно придётся сериализовать и нужно поддерживать в принципе всё что угодно? Ну хотя бы как-то записать объект чтобы не было ошибки и чтобы объект записался в JSON более менее информативно. В этом случае можно вызвать стандартные способы репрезентации объекта. Самое простое это функция repr(). И тут уж как повезёт с тем, как именно разработчик позаботился о таком поведении его класса. Более описательный метод, это закинуть объект в словарь с именем класса и данными инстанса. В общем, возвращаясь к примеру из прошлого поста, мы просто убираем проверку конкретного типа class MySerializer(json.JSONEncoder): def default(self, obj): return {'type': obj.__class__.__name__, 'data': vars(obj)} Функция vars() аналогична обращению к атрибуту __dict__ Теперь любой ранее неизвестный объект будет успешно сериализован... или нет? >>> json.dumps(datetime.datetime.now(), cls=MySerializer) AttributeError: 'datetime.datetime' object has no attribute '__dict__ Так уж вышло, что не любой объект имеет атрибут __dict__. В таком случае используем repr() class MySerializer(json.JSONEncoder): def default(self, obj): return {'type': obj.__class__.__name__, 'data': getattr(obj, '__dict__', repr(obj))} То есть мы пробуем забрать данные инстанса, и если не получается то вызываем стандартное строковое представление объекта в надежде что разработчики позаботились о нём. >>> json.dumps(datetime.now(), cls=MySerializer) '{"type": "datetime", "data": "datetime.datetime(...)"}' ⚠️ PS: Не могу сказать что решение идеально. Такую неявную сериализацию можно сравнить с замалчиванием ошибок. try: do_something() except: pass То есть в какой-то момент вы будете получать совершенно бесполезные данные и придётся искать где это происходит. Если не определён метод __repr__ то вы получите что-то вроде такого <__main__.MyClass object at 0x00000147CE48CBC8> Что с этим делать? Непонятно! 🤔 Скорее всего найдутся люди, которые осудят такой подход. Может выбрасывать ошибку в случае отсутствия метода __repr__ в классе? #libs#tricks
Објавено 7 дек.
JSON — весьма удобный формат для передачи или хранения данных. Но у него есть одна особенность: по умолчанию он умеет сериализовать только стандартные типы данных Python, такие как int, float, list, dict и тд. Как только появляется какой-либо класс мы терпим фиаско с ошибкой что-то вроде TypeError: Object of type MyClass is not JSON serializable Причём не поддерживаются даже стандартные классы типа datetime или re.Pattern😢. Повезло лишь некоторым классам, которые предназначены для хранения данных, например namedtuple или defaultdict. Как сделать так, чтобы любой объект смог сериализоваться в JSON? Обычный подход это создать свой класс-сериализатор, где и будет прописан алгоритм превращения объектов в строку, словарь или что-то обычное для JSON Допустим, у меня есть мой класс: class MyClass: def __init__(self): self.x = 100 Создадим сериализатор который умеет понимать такой тип объекта. Его задача: сохранить имя типа и данные инстанса чтобы потом можно было восстановить объект обратно. Для этого переопределим метод default(), котоырй по умолчанию выбрасывает ошибку когда объект не поддерживается. class MySerializer(json.JSONEncoder): def default(self, obj): if isinstance(obj, MyClass): return {'type': MyClass.__name__, 'data': obj.__dict__} return super().default(obj) Пробуем закодировать объект в JSON используя мой сериализатор >>> mc = MyClass() >>> json.dumps(mc, cls=MySerializer) '{"type": "MyClass", "data": {"x": 100}}' Отлично, теперь json понимает мой тип! 😎 #libs
Hashtags
Објавено 2 дек.
В стандартных библиотеках Python есть средства для текстового процессинга. Например difflib, fnmatch или тотже re. Но в реальной работе они оказываются "многословны" при при поиске нечётких совпадений строк. Например, если вы пишете голосовой помощник и вам требуется определить произнесённые ключевые слова. Или в вашем телеграм-боте пользователь вводит определённые слова и возможны ошибки или сокращения. Вместо того чтобы писать большой список вариантов каждого слова следует применять нечёткое совпадение в тексте. Для этого хорошо подойдёт библиотека fuzzywuzzy. Она может посчитать на сколько один текст похож на другой или найти из списка строк самое похожее на заданный текст. Пример поиска имени месяца по введённому юзером тексту. Список месяцев берём из примера прошлого поста. >>> from fuzzywuzzy import process >>> import calendar >>> months = list(calendar.month_name)[1:] # достаём все совпадения. Функция возвращает само слово и степень похожести >>> process.extract('январ', months, limit=3) [('Январь', 91), ('Март', 44), ('Сентябрь', 40)] # можно сразу взять самое похожее совпадение >>> process.extractOne('фев', months) ('Февраль', 90) Конечно, на этом возможности не ограничены. Можно искать в текстах плагиат, анализировать текст на статистику похожих слов, создать автокомплиты с исправлениями в текстовых редакторах и тд. #libs
Hashtags
Објавено 30 ное.
Как получить список месяцев или дней недели на русском языке? Можно "нахардкодить" список прямо в своём коде😖. Но лучше воспользоваться стандартным модулем calendar установив нужную локализацию. >>> import calendar >>> import locale >>> locale.setlocale(category =locale.LC_ALL, locale="Russian") >>> print(list(calendar.month_name)[1:]) ['Январь', 'Февраль', 'Март', ... >>> print(list(calendar.day_name)) ['понедельник', 'вторник', 'среда', ... >>> print(list(calendar.day_abbr)) ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'] Модуль datetime тоже будет с переводом >>> import datetime >>> datetime.date.today().strftime('%B %Y года, %A') 'Ноябрь 2020 года, суббота' (кстати, именно так получается список month_name) А вот так можно распечатать календарь на год на русском прямо в терминал: print(calendar.LocaleTextCalendar(locale="Russian_Russia").formatyear(2021)) #libs#tricks
Објавено 23 ное.
Python + bash Если вам часто требуется запускать shell команды из Python-кода, какой способ вы используете? Самый низкоуровневый это функция os.system(), либо os.popen(). Рекомендованный способ это subprocess.call(). Но это всё еще достаточно неудобно. Советую обратить своё внимание на очень крутую библиотеку sh. Что она умеет? 🔸 удобный синтаксис вызова команд как функций # os import os os.system("tar cvf demo.tar ~/") # subprocess import subprocess subprocess.call(['tar', 'cvf', 'demo.tar', '~/']) # sh import sh sh.tar('cvf', 'demo.tar', "~/") 🔸 простое создание функции-алиаса для длинной команды fn = sh.lsof.bake('-i', '-P', '-n') output = sh.grep(fn(), 'LISTEN') в этом примере также задействован пайпинг 🔸 удобный вызов команд от sudo with sh.contrib.sudo: print(ls("/root")) Такой запрос спросит пароль. Чтобы это работало нужно соответствующим способом настроить юзера. А вот вариант с вводом пароля через код. password = "secret" sudo = sh.sudo.bake("-S", _in=password+"\n") print(sudo.ls("/root")) Это не все фишки. Больше интересных примеров смотрите в документации. Специально для Windows💀 юзеров #libs#linux
Објавено 4 ное.
Выполните этот код: >>> import __hello__ Hello world! Данный модуль называют пасхалкой в Python, но это не совсем так. Это модуль добавлен для тестирования и демонстрации "заморозки" модулей. # This file exists as a helper for the test.test_frozen module. Вот так можно убедиться что модуль "заморожен": >>> import imp >>>> imp.is_frozen('__hello__') True Исходник этого модуля можно посмотреть здесь У него так же есть модуль-брат __phello__исходник которого здесь >>> import __phello__ >>> print(__phello__.initialized) True >>> from __phello__ import spam >>> print(spam) <module '__phello__.spam' (frozen)> >>> print(__phello__.__loader__) <class '_frozen_importlib.FrozenImporter'> А вот здесь лежит файл который собирает эти модули В данном случае "ЗАМОРОЖЕННЫЙ" означает что модуль вшит непосредственно в библиотеку python3.dll (Windows) или libpython3.so (Linux). #libs
Hashtags
Објавено 2 ное.
Модуль Qt․py это не просто текстовый модуль, его компоненты генерируются на лету в зависимости от ситуации. Поэтому ваша любимая IDE не сможет качественно сообразить автокомплиты под этот модуль. Решение здесь более чем очевидно, надо сделать stubs-файлы. Это файлы с расширением .pyi, описывающие содержимое модуля но не имеющие рабочего кода. Ну что, готовы потратить пару месяцев своей жизни чтобы описать все классы Qt и их методы? 😭 Расслабьтесь, за вас это уже сделали добрые люди. Спасибо Fredrik Averpil ! Качаем здесь ⬇️ https://github.com/fredrikaverpil/Qt.py/tree/stubs/stubs/Qt Не думаю что стоит устанавливать Qt․py из этого репозитория. Он там не обновляется. Так что забираем только файлы .pyi. За актуальность этих файлов тоже не ручаюсь, но большинство методов там имеются. Установка: 🔸 Вариант 1: - находим куда установлен сам модуль Qt․py, это будет одинокий файл который так и называется Qt․py - кидаем директорию рядом с ним (если есть доступ на запись). Должно получиться так: 📁 site-packages\ 📄 Qt.py 📁 Qt\ ... 🔸 Вариант 2 - копируем директорию Qt куда угодно - пробиваем путь к ней в настройках энвайронмента в переменную PATH так, чтобы путь был ДО директории Qt. Закинуть можно и в свою домашнюю директорию. Если скопируете сюда: ~/stubs/Qt То переменную пишем так export PATH=~/stubs:${PATH} После этого IDE должна распарсить stubs-файлы и автокомплиты появятся 😎 #qt#libs#tricks
Објавено 28 окт.
Для тех кто пишет расширения на PyQt/PySide для CG-софтов. Когда я только начинал писать тулзы под Maya (тогда еще версия 2010-2011) мне приходилось ручками ставить PyQt4 под Maya. Даже написал мануалы по установке на своём сайте. Но потом стал доступен из коробки PySide и позже он обновится до PySide2. Для некоторых систем была поддержка PyQt5. И как простому разработчику поддерживать этот зоопарк? Ведь хочется чтобы тул работал на любой версии (вы тоже делали модуль что-то типа import_qt.py?😁) На помощь приходит проект Qt.py который поставил себе цель унифицировать использование Qt-биндингов вне зависимости от среды где запускается код. Те, кто давно пишут на Qt, скорее всего знают этот проект. Он стал стандартом для CG-индустрии и используется в топовых студиях и проектах. Qt․py помогает запускать один и тот же код на разных платформах с разными вариантами Qt-библиотек. Это может быть как интеграция в CG-софт, так и переносимость стендалонов между разными платформами с разными версиями Python. Я решил рассказать о некоторых особенностях работы с этой библиотекой. Сегодня о том, как установить и использовать Qt․py и что это вам даёт. Установка pip install Qt.py Чтобы начать использовать Qt․py в коде достаточно заменить импорт вашего варианта Qt-биндинга на Qt․py from [PySide|PyQt4|PySide2|PyQt5] import QtWidgets => from Qt import QtWidgets Теперь ваш код будет поддерживать любой вариант биндинга Qt в Python. При этом не потребуется использовать if-else конструкции под разные версии. Все вызовы теперь одинаковы. Всё что нужно сделать, это написать его по правилам PySide2. Именно эта версия была взята за основу. Приоритет импорта такой: 1. PySide2 2. PyQt5 3. PySide 4. PyQt4 Что именно загрузилось можно посмотреть в переменной __binding__ >>> import Qt >>> Qt.__binding__ 'PySide2' Приоритет имопрта можно изменить через переменные QT_PREFERRED_BINDING и QT_PREFERRED_BINDING_JSON. Причем под каждый проект оверрайды можно настраивать индивидеально. #qt#libs
Објавено 28 сеп.
В Python2 была интересная возможность кодировать строку с помощью ZIP-архивации. Ведь что такое кодирование? Просто преобразование данных из одного вида в другой. Компрессия это тоже просто другая форма данных. Представление данных в виде ZIP просто и легко сокращает размер этих данных. Это бывает полезно или даже критично при передаче данных по медленным каналам. # Python2 >>> my_str = 'Hello ZIP' >>> my_zip_str = my_str.encode('zip') >>> print my_zip_str xЬєH═╔╔WИЄ♀ ☼╨♥️ Да, в результате это нереально прочитать, но мы же понимаем что это просто такая форма данных. Наша строка всё еще где-то там. Пробуем преобразовать обратно. >>> my_zip_str.decode('zip') 'Hello ZIP' Всё на месте! Пытливые умы заметили, что мы нифига не сэкономили 😭 >>> print len(my_str), len(my_zip_str) 9, 17 Увеличили размер почти в 2 раза! Да, это так. Но вся сила ZIP раскрывается на больших данных >>> my_str = 'Hello ZIP ' * 100 >>> my_zip_str = my_str.encode('zip') >>> print len(my_str), len(my_zip_str) 1000 27 Чем больше строка и чем больше в ней повторов, тем лучше она сожмётся. В данном случае в 37 раз меньше данных. Да, круто, но к чемуто это я? Дело в том что в Python3 этот код не сработает. Во-первых, кодек переименован в "zlib_codec", во-вторых подобный код вызовет ошибку и отправит нас в модуль codecs. my_str.encode('zlib_codec') LookupError: 'zlib_codec' is not a text encoding; use codecs.encode() to handle arbitrary codecs Данный функционал можно повторить и в Python3 но кода получится куда больше. И что теперь? Неужели я предлагаю заняться "некрокодингом" на Python2? Нет, не нужно тревожить пенсионера 🚑. Всё будет ясно вследующем посте. #libs
Hashtags
Објавено 25 сеп.
Чем отличаются эти термины? Шифрование Кодирование Хеширование 🔸Шифрование Обратимое преобразование информации с целью скрыть её содержимое. Шифруются данные с помощью секретного ключа, без которого расшифровать данные в исходное состояние невозможно. Примеры использования: - Шифрование бинарных файлов и архивов с помощью пароля - Шифрование текста 🔸Кодирование Это преобразование данных из одного вида в другой. При этом содержимое данных остаётся прежним, меняется лишь форма (способ представления данных). Всегда можно вернуть данные в прежний вид с помощью обратного кодирования (декодирование). Примеры использования: - Кодирование текста из ASCII в UTF-8 - Кодирование изображения из BMP в TIF - Кодирование строки в байты 🔸Хеширование Необратимое преобразование данных в строку определённой длины. Содержимое хеш-строки строго зависит от исходных данных и сильно изменяется даже если в данных изменить 1 бит. Хеширование всегда выдаёт одинаковый результат при одинаковых исходных данных. "Дехешировать" строку обратно в данные невозможно. Примеры использования: - Хранение пароля в базе данных в виде хеш-строки не раскрывает пароль и оставляет возможность проверять правильность введённого пользователем пароля. - Хеш-строку файла еще называют контрольной суммой. Позволяет быстро проверить одинаковы ли файлы сравнив лишь хеши этих файлов. Даже если один пиксель в картинке изменится, контрольная сумма будет другой. Очень полезно для проверки необходимости обновления файлов по сети или поиска файлов. 🐍Модули 🔹 Для работы с криптографией в Python используются внешние модули cryptography PyCrypto 🔹Хеширование и другие манипуляции с паролями и токенами crypt hashlib secrets hmac Информация здесь 🔹Всё что связано с кодировками codecs #libs
Hashtags
Објавено 23 сеп.
Стартап-скрипт это удобное место для изменения внешнего вида REPL. Например замена символов строки приглашения или добавление автокомплитов по TAB. Давайте заменим символы строки приглашения. Для этого нужно поменять переменные sys.ps1 и sys.ps2. Символами может быть даже эмодзи. Напишите это в стартап-скрипте: import sys sys.ps1 = '⏩ ' sys.ps2 = '⏳ ' А как насчет динамически изменяемой строки? Это тоже можно. Создаём класс, наследованный от строки и определяем что он возвращает при печати. import sys class PS1(object): def __init__(self): self.s = True def __str__(self): self.s = not self.s return '\033[97m█◣ \033[0m ' if self.s else '\033[34m█◤ \033[0m ' sys.ps1 = PS1() sys.ps2 = '▼ ' Попробуйте поработать с этим) ___________ Еще пример python-startup скрипта с просторов гитхаба. И пример от меня, рандомный смайл на каждой строке 😂 #tricks#libs