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

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

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

当前筛选 #tricks清除筛选

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

В Python можно делать рекурсивные связи. Например, список вложенный сам в себя: >>> x = [] >>> x.append(x) >>> print(x) [[...]] Словари вложенные друг в друга: >>> a = {} >>> b = {} >>> a['b'] = b >>> b['a'] = a >>> print(a) {'b': {'a': {...}}} Зачем это нужно? Лично я пока не сталкивался с подобными задачами, но, скорее всего, они есть) #tricks

1,150 views

Hashtags

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

🖨Зачем подменять стандартную функцию print()? В Python3 директива print() стала функцией, то есть объектом, с которым мы можем делать что угодно. Во 2-м Python мы такого лишены. Как можно изменить стандартное поведение этой функции? Обычно подменяют объект sys.stdout, что работает и во 2-м. Но будем действовать через builtins. При этом изменим только функцию print(), а stdout останется прежним. Что нам потребуется сделать? 1. Создать функцию, которая заменит print и не сломает стандартное поведение 2. Подменить объект builtins.print import builtins _print = builtins.print builtins.print = lambda *a, **kw: _print(*a, **kw) В примере выше я заменил print но не добавил никакого функционала. Сейчас print() работает как и прежде, только через посредника. Давайте думать варианты. 🔸Изменить аргументы по умолчанию. Порой во время разработки приложения требуется именно распечатывать информацию в консоль через print(). Если это сервер, то всякий раз приходится дописывать flush=True чтобы очищался буфер вывода и мы видели в консоли текст сразу же. Давайте сделаем так чтобы этот аргумент по умолчанию был True. Тогда лямбда будет выглядеть так: lambda *a, **kw: _print(*a, **{**kw, 'flush': True}) 🔸Поиск принтов в коде. Приходилось пару раз рефакторить код сервера с очень запутанной структурой. На этапе избавления от принтов я не мог найти где распечатывается пустой список?! Ни одного принта по проекту нет а он распечатывается😢 Давайте заставим функцию print() сообщать нам где она находится. import builtins, sys _print = builtins.print def _located_print(*args, **kwargs): _print(*args, **kwargs) f = sys._getframe().f_back _print('=> File:', f.f_code.co_filename, '| line', f.f_lineno) builtins.print = _located_print Выйдет примерно так: >>> print('Hello') Hello => File: /path/to/script.py | line: 1 🔸Как-либо модифицировать все принты. Например добавлять что-то в начале, изменять цвет, заменять символьные смайлы на юникод и тд. Вот пример от меня. В Python 3.8 добавили возможность через f-string распечатывать имя переменной вместе с её значением: >>> value = 123 >>> print(f"{value=}") value=123 Аналогичный функционал я повторил через подмену функции print(). 🌎 Полный код здесь 🔸Отключить принт повсеместно! Да, бывает и такое нужно). Кроме простого отключения (код сами догадайтесь какой) можно заменять всё на одинаковое сообщение, сделав принт бесполезным. Чтобы не использовали принт в проектах! import builtins _print = builtins.print builtins.print = lambda *a, **kw: _print("Don't use prints!") Получится что-то такое >>> print('Debug message') Don't use prints! Но это больше похоже на шутку, реализовать которую помогут стартап скрипты, о которых юзер не догадывается 😝 🔸Заменить все принты на нормальное логирование. Тоже вариант, но не особо полезный. Лучше писать нормальный логгинг чем так "костылять". Ну а для тренировки можно попробовать реализовать и этот вариант. Думаю, сами справитесь) _________________________ Стоить ещё учесть пару моментов: - эту подмену следует делать в самом начале работы приложения, в скрипте с которого начинается запуск. Удобно делать через стартап скрипт. Исходники при этом менять не требуется. - функция изменится повсеместно во всех модулях на время сессии - stdout не изменён, то есть обычный логгер будет писать в консоль нормально. - Мы подменяем функцию на другую, а значит help(print) не покажет нам документацию, __name__ будет неверный и тд. Так что не забывайте использовать functools.wraps - в моих примерах кое-где не обрабатывается kwargs. Не копируйте вслепую, всегда понимайте что делаете или не делайте вовсе. Присылайте в коменты свои варианты! 😎 #tricks

1,200 views

Hashtags

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

Те, кто в Python не первый день, хорошо знают, что на число можно умножить не только число, но и другие типы. Главное, чтобы у этих типов была реализация такой операции. # list >>> [1] * 3 [1, 1, 1] # tuple >>> (2, 3) * 3 (2, 3, 2, 3, 2, 3) # string >>> "A" * 3 "AAA" Так работает полиморфизм стандартных типов. Интересно здесь то, что это сработает и в том случае, когда порядок операндов обратный. То есть int умножить на [тип]. # list >>> 3 * [1] [1, 1, 1] # tuple >>> 3 * (2, 3) (2, 3, 2, 3, 2, 3) # string >>> 3 * "A" "AAA" Если хотите реализовать такое поведение в ваших классах то следует помнить два момента: 1. Если множитель справа, то вам нужно реализовать метод __mul__, наш класс это первый операнд, то есть слева. myType * 3 2. Если множитель слева, то вам нужно реализовать метод __rmul__, наш класс это второй операнд, справа. 3* myType Всё тоже самое можно делать и для других математических операторов. И если в этом примере действие и результат будут фактически одинаковыми, то бывают ситуации, когда это не так. Например, при умножении матриц имеет значение порядок операндов. Для других операторов, таких как деление или сдвиг, очень важно кто с какой стороны находится. >>> 2/4, 4/2 (0.5, 2.0) >>> 2<<3, 3<<2 (16, 12) >>> 100%15, 15%100 (10, 15) #tricks#basic

1,310 views

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

Вы знаете, что строки в Python можно кодировать и декодировать в разные кодировки. >>> s = 'Привет' >>> s.encode() # в байты b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82' >>> s.encode('ascii') # в ASCII (если получится) UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128) >>> s.encode('euc_jp') # японская кодировка b'\xa7\xb1\xa7\xe2\xa7\xda\xa7\xd3\xa7\xd6\xa7\xe4' >>> s.encode('hz') # Simplified Chinese b"~{'1'b'Z'S'V'd~}" И другие... А можно ли добавить свою кодировку в этот список? Можно! И сейчас мы это сделаем. Давайте добавим ZIP-кодировку из Python2, о которой мы говорили в прошлом посте. Для начала нам нужен алгоритм. Я хочу сжимать строку через ZIP и после получения байтов преобразовывать их в строку через base64 (можно и без base64). Получится такой код: import zlib, base64 >>> orig = 'hello python' >>> compressed = zlib.compress(orig.encode(), 5) >>> compresed_b = base64.encodebytes(compressed) >>> print(compresed_b) b'eF7LSM3JyVcoqCzJyM8DAB7wBNc=' Да, строка получилась длинней чем была. Но мы помним, что всё будет иначе если исходная строка большая. Обратный алгоритм декодирования >>> compressed = base64.decodebytes(compresed_b) >>> restored = zlib.decompress(compressed).decode() >>> orig == restored True Отлично!😎 Теперь, чтобы создать кодек на базе этих алгоритмов, следует воспользоваться функций codecs.register(). В неё подаётся имя функции которая должна вернуть объект codecs.CodecInfo. Данный объект будет содержать две функции - кодирование и декодирование. Это и будет наш кодек. Давайте назовём наш кодек просто "z". Тогда его создание будет выглядеть как-то так: import zlib, base64, codecs def z_encode(data): return base64.encodebytes(zlib.compress(data.encode(), 5)), 0 def z_decode(data): return zlib.decompress(base64.decodebytes(data)).decode(), 0 def z_search(encoding_name): return codecs.CodecInfo(z_encode, z_decode, name='z') codecs.register(z_search) Тестим! >>> s = 'Hello New codec!' >>> s_enc = s.encode('z') >>> print(s_enc) b'eF7zSM3JyVfwSy1XSM5PSU1WBAAvxwV+\n' Теперь обратно >>> s_enc.decode('z') 'Hello New codec!' А теперь практика!. Сжимаем JSON со списком файлов >>> from pathlib import Path >>> import json >>> files = list(map(str, list(Path('~/Documents').expanduser().glob('**/*')))) >>> print(len(files)) 5390 >>> text = json.dumps(files) >>> print(len(text)) 513839 >>> enc = text.encode('z') >>> print(len(enc)) 53317 Профит почти в 10 раз! Можно еще уменьшить размер если обойтись без BASE64. Но тогда вы получите чистые байты и как строку их передать уже не получится. PS. А вот стандартный и самый короткой способ для ZIP-сжатия байтов в Python3 >>> import codecs >>> codecs.encode(my_bytes, 'zip')) (спасибо @amarovita за пример кода) #tricks

1,210 views

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

1,150 views

Hashtags

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

Выполнение функции перед выходом. Как вызвать функцию перед выходом из программы в Python? Для этого нужно использовать модуль atexit. Он регистрирует функции, которые выполняются перед завершением процесса интерпретатора. import atexit def before_exit(): print('BEFORE EXIT') atexit.register(before_exit) Теперь попробуйте завершить процесс и увидите сообщение >>> exit() BEFORE EXIT 🔸 Для регистрации такой функции в REPL пригодится startup скрипт. 🔸 Этот функционал работает и в обычном режиме без интерактивной консоли. #tricks#libs

1,140 views

Hashtags

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

Startup скрипт для REPL. Как выполнить скрипт сразу после старта интерактивной консоли Python? Для начала понять бы зачем это может понадобиться. А причины бывают достаточно весомые - автоматизировать одни и те же действия которые вы повторяете при старте REPL - кастомизировать сам REPL - объявление энвайромента или констант для ручного дебага или тестирования итд... В общем, как-то ускорить свою работу с REPL. Как пример поведения, команда shell_plus в пакете django-extensions, которая перед запуском шела импортит всё самое необходимое. Есть два способа это сделать 🔸Флаг i Запишите все действия в скрипт, например startup.py, и запускайте консоль такой командой: python -i startup.py Флаг -i означает, что после выполнения скрипта инетрпретатор не завершит процесс а перейдёт в интерактивный режим, то есть обычный RELP. 🔸Переменная окруженияPYTHONSTARTUP Еще до старта самого Python объявляем переменную PYTHONSTARTUP, в которую следует указать путь к скрипту. Сразу после запуска REPL но до первого приглашения к вводу команды ваш скрипт исполнится. Windows set PYTHONSTARTUP=C:\path\to\startup.py Linux export PYTHONSTARTUP=/path/to/startup.py Теперь запуск REPL будет исполнять указанный файл Всё это относится только к интерактивной консоли REPL! Если вам нужен аналогичный функционал для обычного запуска приложения, то его нет. Так как это легко реализовать штатными средствами, добавив нужные импорты в начало вашего приложения. #tricks

1,290 views

Hashtags

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

Как создать новый тип объекта? Очень просто! Cоздайте новый класс. class MyType: pass Такой способ подходит для создания класса, статично записанного в скрипт. Но бывают случаи когда мы заранее не знаем какие атрибуты и методы будут у класса. То есть требуется динамическое создание класса во время исполнения кода. Для чего? Самое очевидное использование это Mock-объекты. Когда нам нужно подменить оригинальный объект заглушкой. Регулярно используется в авто тестах и в генераторах документации. Есть даже специальный класс для этого unittest.mock.Mock. Можем на лету создавать класс и настраивать его поведение. >>> from unittest.mock import Mock >>> dyn_obj = Mock() >>> dyn_obj.return_value = 123 >>> print(dyn_obj()) 123 Также можно создать инлайн тип с помощью функции type(). MyType = type("MyType", (object,), {"func": lambda: 123, "attr": 321}) Первый аргумент это имя нового объекта. Второй аргумент это список родительских классов. Третий, это неймспейс объекта, то есть его методы и атрибуты. В неймспейсе можно указывать как просто данные так и функции. Однажды, для совместимости со старым кодом потребовалось немного изменить имеющийся класс. Был переименован метод и нужно было временно сделать заглушку. Писать отдельный класс не хотелось, а нужен был именно класс а не инстанс, к тому же не надолго. Вот тут и пригодился данный трик. # старый класс class OldClass: def old_method(self): return 123 # вариант с переопределением старого класса class OldClass(OldClass): def new_method(self): return self.old_method() # тоже самое но с динамически созданным типом непосредственно в том месте где он требуется OldClass = type('NewClass', (OldClass,), {'new_method': lambda self: self.old_method()}) >>> obj = OldClass() >>> print (obj.new_method(), obj.old_method()) (123, 123) Пример из практики #1 Был случай, когда я написал собственную реализацию запросов на сервер. Некий аналог requests, но очень простой, на чистых сокетах. Это требовалось потому, что в исполняемой среде была версия Python, в которой никак не хотел работать оригинальный requests, да и слишком тяжёлый он был. При этом интерфейс ответа требовалось сохранить как в requests, но не всегда а по запросу, то есть динамически. Пример из практики #2 Для некоторых, не совсем логичных, но всё же целей, требовалось воссоздавать типы, которые прилетели в виде JSON-данных. Структура типа и данные инстанса были в этом JSON. Оставалось только пересобрать класс и создать инстанс. Некое подобие pickle, только немного иначе. Но это уже достаточно сложные примеры для поста. Пожалуй, код опустим)))😊 #tricks

1,060 views

Hashtags

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

Функция round() может округлять не только дробную часть но и целую. Для этого нужно использовать отрицательное значение. >>> round(12345.6789, 2) 12345.68 >>> round(12345.6789, -2) 12300.0 #tricks

794 views

Hashtags

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

Посмотрите на эту функцию def my_func(): x = 1 y = 2 ... return x+y Если я запущу её в таком виде, какую ошибку получу? Похоже на пример из урока. Скорее всего будет SyntaxError из-за троеточия (...). Но если вы выполните этот код... ОШИБКИ НЕ БУДЕТ😳! Почему❓ Всё дело в том что троеточие это одна из стандартных констант в Python, называется Ellipsis. Такая же как None, True или False. Это не какой-то тип данных, для него нет каких-либо специальных операторов. Нигде в стандартной библиотеке он не используется. Хотя, может в распечатке объектов с бесконечной рекурсией (но это не точно). >>> x = [] >>> x.append(x) >>> print(x) [[...]] Так где же применять эту штуку? Прежде всего, стоит заметить что этот объект кажется достаточно неуместным для Python. Ведь Python такой логичный и минималистичный и тут вдруг бесполезная константа которая нигде не встречается! Но всё же давайте посмотрим как эта константа может быть использована. 🔸Особый синтаксис срезов В документации написано что Ellipsis используется для "extended slicing syntax". Это значит что он используется в слайсинге, но не в стандартном. Его можно применить в расширенном слайсинге, то есть когда переопределяете метод __getitem__ в ваших классах. >>> class C(object): >>> def __getitem__(self, item): >>> return item Если вы попробуем получить нечто по индексу то в item прилетает этот индекс >>> C()[1] 1 Но если передать в квадратные скобки несколько аргументов, то в item прилетает кортеж >>> C()[1, 10] (1, 10) Вам остаётся решить как обработать такие аргументы и вернуть "срез" вашего типа. Ну и ничто не мешает нам использовать (...) в качестве "синтаксического сахара" для реализации какой-то особой логики. >>> C()[1, ..., 10] (1, Ellipsis, 10) >>> C()[3:...] slice(3, Ellipsis, None) Вам остаётся лишь решить как именно обработать такой запрос. Например так: >>> class C(object): >>> def __getitem__(self, item): >>> if item is Ellipsis: >>> return "RETURN ALL" >>> return "RETURN BY INDEX" >>> C()[...] RETURN ALL Именно так сделано в numpy 🔸Аналог для pass Сравните две записи def some_func(): pass def some_func(): ... По-моему, вторая запись более ясно говорит, что здесь что-то имеется в виду и это надо дописать 😉. Но, несмотря на то, что Ellipsis был доступен еще с древних версий 2.Х, такая запись сработает только в Python3. Для 2го троеточие доступно только в квадратных скобках. 🔸Заглушка для значений по умолчанию def func(x=...): ... Альтернатива None? Совсем уж сомнительно 😖, но работает как визуальный триггер для разработчика) Всё, больше идей пока нет) На последок вот такая странная фиговина. Что покажет этот print() ??? #tricks

802 views

Hashtags

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

Роняем Python в одну строку Как имитировать ошибку Segmentation Fault в коде и уронить процесс интерпретатора? 🔸 Форсированно завершаем процесс через kill и exit code 11. __import__('os').kill(__import__('os').getpid(), 11) Но это просто быстрый выход. 🔸 Делаем что-то что вызовет ошибку со страшным сообщением что всё сломалось! Перегружаем стек вызова рекурсией, заведомо увеличив лимит до не приличия высоко. __import__('sys').setrecursionlimit(1<<30);f=lambda f:f(f);f(f) 🔸Ломаем парсер AST __import__('ast').literal_eval('1+1'*10**6) Все эти вызовы приводят к такой ошибке: Process finished with exit code 139 (interrupted by signal 11: SIGSEGV) Хм... а зачем это может быть нужно? Может кому-то хотите устроить подлянку 👹, а может тестируете дебагер. Таким падением можно "указать" юзеру что он зашел куда не следует чтобы больше так не делал 😨 Всё это надуманные примеры. Но тем не менее, теперь вы теперь знаете как это сделать😉. PS. Не советую использовать этот код в рабочих проектах! #tricks

825 views

Hashtags

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

Как узнать кто вызвал функцию? Порой требуется выяснить, кто именно вызвал конкретную функцию? Конечно, можно запустить дебаг и выполнять построчно логику отслеживая откуда мы пришли в конкретную точку. Но это долго. Чтобы просто узнать имя функции которая вызвала текущую функцию можно сделать так. # myscript1.py import inspect def function1(): # распечатаем имя вызывающей функции print('Called from:', inspect.stack()[1][3]) def function2(): function1() function2() Запускаем python3 myscript1.py Called from: function2 Но так мы увидим лишь имя предыдущей функции. А как узнать полный список вызовов? Traceback удобен тем, что показывает цепочку вызовов, которая привела к ошибке. Но модуль traceback также позволяет распечатать стек вызовов не выбрасывая исключение. # myscript2.py import traceback def function1(): ... traceback.print_stack() ... def function2(): function1() function2() Запускаем файл python myscript2.py File "myscript.py", line 9, in <module> function2() File "myscript.py", line 7, in function2 function1() File "myscript.py", line 4, in function1 traceback.print_stack() #tricks

760 views

Hashtags

12•••7891011•••1415
ПретходнаСтраница 9 од 15Следна