Давайте немного прокачаем стандартный словарь. Зачем? А потому что Python это позволяет)))
Добавим возможность работать с ключами словаря как с атрибутами объекта. Для этого нам потребуется определить три метода:
Наследоваться будем от стандартного словаря
Но помните , если Python позволяет это делать, это не значит что это можно делать 👮♂️!
Подобные эксперименты лучше оставить для развлечения и не допускать в прод.
Хорошая разминка, не более того.
#tricks
Добавим возможность работать с ключами словаря как с атрибутами объекта. Для этого нам потребуется определить три метода:
__getattr__ - получение атрибута__setattr__ - изменение атрибута__delattr__ - удаление атрибутаНаследоваться будем от стандартного словаря
class _AttribDict(dict):Подменяем стандартный словарь в одну строку
def __getattr__(self, name):
return self[name]
def __setattr__(self, name, value):
self[name] = value
def __delattr__(self, name):
del self[name]
__import__('builtins').__dict__['dict'] = _AttribDict
Всё, можно тестить 😬>>> obj = dict(name='Object1', id=14)Выглядит как обычный словарь
>>> print(obj)
{'name': 'Object1', 'id': 14}
>>> obj['name'] = 'Thing 1'Изменение ключей работает. То есть стандартное поведение не сломали. А теперь попробуем обратиться к атрибутам
>>> print(obj['name'])
Thing 1
>>> print(obj.name)Пробуем изменить ключ через атрибут
Thing 1
>>> obj.name = 'Thing 2'И удаляем ключ через атрибут
>>> print(obj.name)
Thing 2
del obj.nameТеперь с ключами словаря можно работать и обычным способом:
print(obj)
{'id': 14}
dict['key']и как с атрибутами:
dict.keyВыглядит интересно.
Но помните , если Python позволяет это делать, это не значит что это можно делать 👮♂️!
Подобные эксперименты лучше оставить для развлечения и не допускать в прод.
Хорошая разминка, не более того.
#tricks
А знаете ли вы, что в Bash есть особая переменная "$_" ? Из описания можно узнать, что в переменную "$_" помещается последний аргумент последней команды.
Что-то знакомое? Конечно, в Python есть примерно тоже самое.
Мы знаем, что дата первого релиза Bash (8 июня 1989) несколько раньше чем первый релиз Python (20 февраля 1991). Если учесть, что по задумке автора Python это агрегация самых успешных практик отовсюду, можно ли сказать что именно эта фишка к нему пришла из Bash? Ни подтверждения ни опровержения я пока не нашел.
Давайте просто считать это совпадением 😉
А как это может быть полезно в Bash?
Приведу простой пример, который оценят те, кто часто работает в терминале.
Допустим, нам надо создать директорию и зайти в неё. Что обычно делаем?
PS.
Символы "&&"" разделяют команды и означают, что требуется выполнить вторую команду только если первая завершилась успешно.
#linux #tricks
Что-то знакомое? Конечно, в Python есть примерно тоже самое.
Мы знаем, что дата первого релиза Bash (8 июня 1989) несколько раньше чем первый релиз Python (20 февраля 1991). Если учесть, что по задумке автора Python это агрегация самых успешных практик отовсюду, можно ли сказать что именно эта фишка к нему пришла из Bash? Ни подтверждения ни опровержения я пока не нашел.
Давайте просто считать это совпадением 😉
А как это может быть полезно в Bash?
Приведу простой пример, который оценят те, кто часто работает в терминале.
Допустим, нам надо создать директорию и зайти в неё. Что обычно делаем?
:~$ mkdir foldernameКак это сделать в одну команду?
:~$ cd foldername
:~/foldername$
:~$ mkdir foldername && cd foldernameУже лучше, но хочется короче, без повторов. Вот тут и пригодится наша переменная. Напомню, что в неё сохраняется последний аргумент предыдущей команды.
:~/foldername$
:~$ mkdir foldername && cd $__________________
:~/foldername$
PS.
Символы "&&"" разделяют команды и означают, что требуется выполнить вторую команду только если первая завершилась успешно.
#linux #tricks
Продолжаем знакомиться.
В какой сфере вы работаете или хотите работать?
В какой сфере вы работаете или хотите работать?
Final Results
22%
🖥 Десктоп приложения
19%
📱 Мобильные приложения
48%
🌍 Web-приложения
27%
💥 Компьютерная графика
18%
📈 Аналитика
29%
🤖 Машинное обучение
15%
🤔 Что-то другое
Python Заметки pinned «Продолжаем знакомиться.
В какой сфере вы работаете или хотите работать?»
В какой сфере вы работаете или хотите работать?»
Метод строки isidentifier() поможет узнать, подходит ли данная строка в качестве имени объекта.
Вполне может заменить самостоятельно придуманную регулярку.
#basic
Вполне может заменить самостоятельно придуманную регулярку.
>>> 'some_name'.isidentifier()Обычное имя переменной
True
>>> '私は手紙です'.isidentifier()Юникод в качестве имени тоже доступен
True
>>> '1_name'.isidentifier()Имя не может начинаться с цифры
False
>>> '੬_name'.isidentifier()Включая все цифры юникода
False
>>> 'some name'.isidentifier()Пробелы недопустимы
False
#basic
Для тех кто пишет расширения на 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 и что это вам даёт.
Установка
При этом не потребуется использовать if-else конструкции под разные версии. Все вызовы теперь одинаковы.
Всё что нужно сделать, это написать его по правилам PySide2. Именно эта версия была взята за основу.
Приоритет импорта такой:
1. PySide2
2. PyQt5
3. PySide
4. PyQt4
Что именно загрузилось можно посмотреть в переменной
#qt #libs
Когда я только начинал писать тулзы под 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Теперь ваш код будет поддерживать любой вариант биндинга Qt в Python.
=>
from Qt import QtWidgets
При этом не потребуется использовать 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
GitHub
GitHub - mottosso/Qt.py: Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5.
Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5. - mottosso/Qt.py
В прошлом посте говоря "Все вызовы теперь одинаковы" я несколько слукавил. Всё-таки есть в этом зоопарке версий некоторая несовместимость вызов которой просто так не унифицировать. Эти моменты вынесены в отдельный модуль QtCompat (compatibility). Там не так много функций но они довольно полезны.
Этот модуль содержит унификаци модуля shiboken2, функций loadUi, translate и несколько переименованных функций классов или изменённую сигнатуру аргументов и возвращаемых значений. Это единственное исключение из правила когда вам потребуется где-то изменить свой код кроме импортов и этот код не похож на обычный код PySide2.
Например, в PyQt4 и PySide есть метод
Полный список можно посмотреть в таблице.
#qt #tricks
Этот модуль содержит унификаци модуля shiboken2, функций loadUi, translate и несколько переименованных функций классов или изменённую сигнатуру аргументов и возвращаемых значений. Это единственное исключение из правила когда вам потребуется где-то изменить свой код кроме импортов и этот код не похож на обычный код PySide2.
Например, в PyQt4 и PySide есть метод
QHeaderView.setResizeModeДля PyQt5 и PySide2 они были благополучно переименованы в
QHeaderView.setSectionResizeModeЧтобы применить этот метод следует использовать такой код
from Qt import QtCompathУнификация загрузки UI файлов:
header = self.horizontalHeader()
QtCompat.QHeaderView.setSectionResizeMode(header, QtWidgets.QHeaderView.Fixed)
# PySide2Хорошо что таких моментов не много и их легко запомнить.
from PySide2.QtUiTools import QUiLoader
loader = QUiLoader()
widget = loader.load(ui_file)
# PyQt5
from PyQt5 import uic
widget = uic.loadUi(ui_file)
# Qt.py
from Qt import QtCompat
widget = QtCompat.loadUi(ui_file)
Полный список можно посмотреть в таблице.
#qt #tricks
GitHub
GitHub - mottosso/Qt.py: Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5.
Minimal Python 2 & 3 shim around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5. - mottosso/Qt.py
Модуль 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
- кидаем директорию рядом с ним (если есть доступ на запись). Должно получиться так:
- копируем директорию Qt куда угодно
- пробиваем путь к ней в настройках энвайронмента в переменную PATH так, чтобы путь был ДО директории Qt.
Закинуть можно и в свою домашнюю директорию. Если скопируете сюда:
#qt #libs #tricks
Поэтому ваша любимая 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\🔸 Вариант 2
📄 Qt.py
📁 Qt\
...
- копируем директорию Qt куда угодно
- пробиваем путь к ней в настройках энвайронмента в переменную PATH так, чтобы путь был ДО директории Qt.
Закинуть можно и в свою домашнюю директорию. Если скопируете сюда:
~/stubs/QtТо переменную пишем так
export PATH=~/stubs:${PATH}
После этого IDE должна распарсить stubs-файлы и автокомплиты появятся 😎#qt #libs #tricks
GitHub
Qt.py/stubs/Qt at stubs · fredrikaverpil/Qt.py
Python 2 & 3 compatibility wrapper around all Qt bindings - PySide, PySide2, PyQt4 and PyQt5. - Qt.py/stubs/Qt at stubs · fredrikaverpil/Qt.py
Выполните этот код:
У него так же есть модуль-брат
В данном случае "ЗАМОРОЖЕННЫЙ" означает что модуль вшит непосредственно в библиотеку python3.dll (Windows) или libpython3.so (Linux).
#libs
>>> import __hello__Данный модуль называют пасхалкой в Python, но это не совсем так. Это модуль добавлен для тестирования и демонстрации "заморозки" модулей.
Hello world!
# 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
Как проверить является ли число чётным или нечётным? Конечно же оператор "остаток от деления"
Но есть еще альтернативный способ, это битовый оператор AND
Распечатайте ряд целых чисел в двоичном формате
А что делает оператор AND (&)? Он возвращает 1 если оба бита числа имеют значение 1.
Учитывая, что у числа 1 в двоичном представлении только один бит равен 1, и он последний, то все остальные биты от любого числа всегда будут возвращать 0.
Таким образом, делая операцию AND любого числа с числом 1 мы всегда получим либо число 0 либо число 1. Это будет зависеть от последнего бита другого операнда.
Выглядит это примерно так
is_odd = bool(x%2)
Если остаток от деления равен 0 то это чётное число, если 1 то нечётное.Но есть еще альтернативный способ, это битовый оператор AND
is_odd = bool(x&1)
Как это работает?Распечатайте ряд целых чисел в двоичном формате
>>> for i in range(0, 7):
>>> print(bin(i))
0b0
0b1
0b10
0b11
0b100
0b101
0b110
Можно заметить, что последние биты чередуются между 0 и 1.А что делает оператор AND (&)? Он возвращает 1 если оба бита числа имеют значение 1.
Учитывая, что у числа 1 в двоичном представлении только один бит равен 1, и он последний, то все остальные биты от любого числа всегда будут возвращать 0.
Таким образом, делая операцию AND любого числа с числом 1 мы всегда получим либо число 0 либо число 1. Это будет зависеть от последнего бита другого операнда.
Выглядит это примерно так
>>> 38 & 1
0
00100110 &
00000001 =
00000000
>>> 227 & 1
1
11100011 &
00000001 =
00000001
#tricksЧто быстрей по производительности, оператор "%" или битовый оператор "&"?
Мы знаем что битовые операторы работают жутко быстро. Но и простые операторы достаточно оптимизированы.
Вот мои тесты на разных платформах (и разном железе, так что смотрим только разницу)
🔸 Windows 10
Не хочу ничего утверждать этой информацией, это просто для заметки. Не так много у меня железа чтобы сделать нормальные тесты и не так много времени чтобы выяснить причину такого поведения.
Мы знаем что битовые операторы работают жутко быстро. Но и простые операторы достаточно оптимизированы.
Вот мои тесты на разных платформах (и разном железе, так что смотрим только разницу)
🔸 Windows 10
>>> timeit.timeit('for x in range(1000): x%2', number=100000)
3.8916409015655518
>>> timeit.timeit('for x in range(1000): x&1', number=100000)
2.883111000061035
🔸 Windows 7 VMWare>>> timeit.timeit('for x in range(1000): x%2', number=100000)
4.5197880999999995
>>> timeit.timeit('for x in range(1000): x&1', number=100000)
5.360066200000006
🔸 Ubuntu 16 VPS>>> timeit.timeit('for x in range(1000): x%2', number=100000)
2.355823243036866
>>> timeit.timeit('for x in range(1000): x&1', number=100000)
2.754374884068966
🔸 Debian 10 VMWare>>> timeit.timeit('for x in range(1000): x%2', number=100000)
2.979030784990755
>>> timeit.timeit('for x in range(1000): x&1', number=100000)
3.54338647100667
🔸 Raspbian 10 (на RaspberryPI 4 с процессором ARM)>>> timeit.timeit('for x in range(1000): x%2', number=10000)
1.1418334439999995
>>> timeit.timeit('for x in range(1000): x&1', number=10000)
1.4677007579999923
Хм, только на Windows 10 оператор & быстрей чем %. Может это связано с тем что это единственный не виртуальный процессор (кроме ARM на малине)?Не хочу ничего утверждать этой информацией, это просто для заметки. Не так много у меня железа чтобы сделать нормальные тесты и не так много времени чтобы выяснить причину такого поведения.
https://github.com/davvid/wg-python3/blob/guidelines/deliverables/guidelines.rst
Гайдлайн по кодингу на Python с учётом версии 2 и 3.
Полезно почитать тем, кто всё еще переходит на Python3.
#2to3
Гайдлайн по кодингу на Python с учётом версии 2 и 3.
Полезно почитать тем, кто всё еще переходит на Python3.
#2to3
GitHub
wg-python3/guidelines.rst at guidelines · davvid/wg-python3
Python 3 Working Group. Contribute to davvid/wg-python3 development by creating an account on GitHub.
Почему рекомендуют каждый импорт делать на новой строке?
Просто так написано в PEP8, скажете вы. Да, это действительно так. Но PEP8 это стилистические рекомендации. Какая практическая польза от такой записи?
Несколько раз мои студенты спрашивали зачем писать длинней когда можно короче? Не это ли один из основных принципов в Python?
Как мы хотим писать:
Объясняю в два этапа:
🔸Перечисление добавлено для возможности импорта через from, когда из модуля требуется импортнуть несколько имён
Было
Допустим, есть два варианта кода
Есть ли у вас еще доводы в пользу каждого импорта на новой строке?
#tricks #pep
Просто так написано в PEP8, скажете вы. Да, это действительно так. Но PEP8 это стилистические рекомендации. Какая практическая польза от такой записи?
Несколько раз мои студенты спрашивали зачем писать длинней когда можно короче? Не это ли один из основных принципов в Python?
Как мы хотим писать:
import os, sys, subprocessКак рекомендуют
import osЕсли "нельзя" писать в одну строку, то зачем добавили такую возможность - перечислять имена импорта?
import sys
import subprocess
Объясняю в два этапа:
🔸Перечисление добавлено для возможности импорта через from, когда из модуля требуется импортнуть несколько имён
from os.path import join, expanduser, sepВ PEP328 добавили возможность перечисление вставлять в скобки чтобы переносить на новую строку без экранирования символа новой строки
Было
from os.path import join, expanduser, \Стало
sep, basename, exists
from os.path import (join, expanduser,🔸 Практическая польза от импортов на разных строках заметна когда вы делаете слияние разных веток кода с конфликтами или просто с изменениями.
sep, basename, exists)
Допустим, есть два варианта кода
# file1.pyЗачем-то нам потребовалось изменить импорт, вместо uuid импортим sys. Что покажет нам diff?
import fnmatch
import time
import json
import uuid
#file2.py
import fnmatch, time, json, uuid
# file1.pyЗаметили разницу? Пусть даже для GIT это совершенно не проблема, кодревью будет происходить удобней с точки зрения человека. Да, это объяснение человека, который сам делает кодревью (и регулярно сам же нарушает эту рекомендацию, о чем потом жалеет 😭)
-import uuid
+import sys
# file2.py
-import fnmatch, time, json, uuid
+import fnmatch, time, json, sys
Есть ли у вас еще доводы в пользу каждого импорта на новой строке?
#tricks #pep
Python Enhancement Proposals (PEPs)
PEP 8 – Style Guide for Python Code | peps.python.org
This document gives coding conventions for the Python code comprising the standard library in the main Python distribution. Please see the companion informational PEP describing style guidelines for the C code in the C implementation of Python.
По аналогии с PEP у Django есть DEP.
Самый интересный для меня на данный момент на это DEP 0009: Async-capable Django. Он про то, как будет внедряться поддержка аснихронности.
Начиная с версии 3 в Django начали появляться асинхронные плюшки. Это всё еще мало чтобы делать асинхронное приложение, но долгий путь начинается с одного маленького шага!
Всё должно пройти в несколько этапов и к 4й версии обещают сделать Django асинхронным!
Что это даёт разработчикам в случае если весь фреймворк станет поддерживать async?
- Ускорение работы web-приложения? Если правильно писать асинхронный код, то да.
- Усложнение кода? Возможно, но фреймворк на то и фреймворк, чтобы прятать сложности где-то внутри. Надеюсь код усложнится не сильно, посмотрим...
И когда нам этого ожидать? Судя по этой схемке Django 4 выйдет в Декабре 2021 года. А это значит, что у вас есть примерно год чтобы научиться понимать асинхронный код, если еще не умеете😁
#django #pep
Самый интересный для меня на данный момент на это DEP 0009: Async-capable Django. Он про то, как будет внедряться поддержка аснихронности.
Начиная с версии 3 в Django начали появляться асинхронные плюшки. Это всё еще мало чтобы делать асинхронное приложение, но долгий путь начинается с одного маленького шага!
Всё должно пройти в несколько этапов и к 4й версии обещают сделать Django асинхронным!
Что это даёт разработчикам в случае если весь фреймворк станет поддерживать async?
- Ускорение работы web-приложения? Если правильно писать асинхронный код, то да.
- Усложнение кода? Возможно, но фреймворк на то и фреймворк, чтобы прятать сложности где-то внутри. Надеюсь код усложнится не сильно, посмотрим...
И когда нам этого ожидать? Судя по этой схемке Django 4 выйдет в Декабре 2021 года. А это значит, что у вас есть примерно год чтобы научиться понимать асинхронный код, если еще не умеете😁
#django #pep
GitHub
GitHub - django/deps: Django Enhancement Proposals
Django Enhancement Proposals. Contribute to django/deps development by creating an account on GitHub.
Python + bash
Если вам часто требуется запускать shell команды из Python-кода, какой способ вы используете?
Самый низкоуровневый это функция
Советую обратить своё внимание на очень крутую библиотеку sh.
Что она умеет?
🔸 удобный синтаксис вызова команд как функций
🔸 удобный вызов команд от sudo
А вот вариант с вводом пароля через код.
Специально для Windows💀 юзеров
#libs #linux
Если вам часто требуется запускать 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
GitHub
GitHub - amoffat/sh: Python process launching
Python process launching. Contribute to amoffat/sh development by creating an account on GitHub.
Делал оптимизацию своей библиотеки кеширования ответов от API. Нужно было выяснить, какие функции заставляют обращаться в API минуя кеш. Мне нужно было видеть полный стек вызова функции запроса в API.
Конечно же первое что приходит в голову это traceback.print_stack(). Но мне не нравится его многословное форматирование. Поэтому я решил сделать свой аналог.
Делается это совсем не сложно, достаточно посмотреть как работает функция print_stack() и сделать аналогично. Все дополнительные функции уже имеются в модуле traceback.
Первым делом достаём фрейм
🔸 Фильтр по имени файла.
Удобно при работе через дебагер, например, в Pycharm. Пропускаются ненужные вызовы.
🔸 Выводить передаваемые аргументы.
Придется сделать свой extract_stack.
🔸 Добавить цвета
Пример реализации здесь. Просто вызовите эту функцию в том месте где требуется увидеть цепочку вызовов.
PS. Да, я знаю на сколько это странный способ дебага. Но такой инструмент потребовался под конкретную ситуацию и он справился хорошо.
PPS:
А теперь суровый хардкод! Всё в одну строку плюс некоторые дополнения в форматировании! (не повторяйте это дома!)
Конечно же первое что приходит в голову это traceback.print_stack(). Но мне не нравится его многословное форматирование. Поэтому я решил сделать свой аналог.
Делается это совсем не сложно, достаточно посмотреть как работает функция print_stack() и сделать аналогично. Все дополнительные функции уже имеются в модуле traceback.
Первым делом достаём фрейм
frame = sys._getframe().f_backТеперь преобразуем его в стек вызовов
stack = traceback.extract_stack(frame)
Теперь у нас есть список инстансов класса FrameSummary в порядке их вызова. Остаётся сделать выводfor item in stack:Получается что-то вроде такого
print(f"{os.path.basename(item.filename)}[{item.lineno}] -> {item.name}")
__main__.py[123] -> <module>Как можно улучшить?
module1.py[456] -> func1
module2.py[789] -> func2
🔸 Фильтр по имени файла.
Удобно при работе через дебагер, например, в Pycharm. Пропускаются ненужные вызовы.
🔸 Выводить передаваемые аргументы.
Придется сделать свой extract_stack.
🔸 Добавить цвета
Пример реализации здесь. Просто вызовите эту функцию в том месте где требуется увидеть цепочку вызовов.
PS. Да, я знаю на сколько это странный способ дебага. Но такой инструмент потребовался под конкретную ситуацию и он справился хорошо.
PPS:
А теперь суровый хардкод! Всё в одну строку плюс некоторые дополнения в форматировании! (не повторяйте это дома!)
print_stack_simple = lambda: print(*[f"{' ' * i}{os.path.basename(item.filename)}[{item.lineno}] -> {item.name}{'()' if not '>' in item.name else ''}" for i, item in enumerate(traceback.extract_stack(sys._getframe().f_back)[:-1])], sep='\n')
#tricksGist
simple_print_stack.py
GitHub Gist: instantly share code, notes, and snippets.
Как получить список месяцев или дней недели на русском языке? Можно "нахардкодить" список прямо в своём коде😖. Но лучше воспользоваться стандартным модулем 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 В стандартных библиотеках Python есть средства для текстового процессинга. Например difflib, fnmatch или тотже re. Но в реальной работе они оказываются "многословны" при при поиске нечётких совпадений строк.
Например, если вы пишете голосовой помощник и вам требуется определить произнесённые ключевые слова. Или в вашем телеграм-боте пользователь вводит определённые слова и возможны ошибки или сокращения.
Вместо того чтобы писать большой список вариантов каждого слова следует применять нечёткое совпадение в тексте.
Для этого хорошо подойдёт библиотека fuzzywuzzy. Она может посчитать на сколько один текст похож на другой или найти из списка строк самое похожее на заданный текст.
Пример поиска имени месяца по введённому юзером тексту. Список месяцев берём из примера прошлого поста.
#libs
Например, если вы пишете голосовой помощник и вам требуется определить произнесённые ключевые слова. Или в вашем телеграм-боте пользователь вводит определённые слова и возможны ошибки или сокращения.
Вместо того чтобы писать большой список вариантов каждого слова следует применять нечёткое совпадение в тексте.
Для этого хорошо подойдёт библиотека 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
PyPI
fuzzywuzzy
Fuzzy string matching in python
JSON — весьма удобный формат для передачи или хранения данных. Но у него есть одна особенность: по умолчанию он умеет сериализовать только стандартные типы данных Python, такие как int, float, list, dict и тд.
Как только появляется какой-либо класс мы терпим фиаско с ошибкой что-то вроде
Как сделать так, чтобы любой объект смог сериализоваться в JSON?
Обычный подход это создать свой класс-сериализатор, где и будет прописан алгоритм превращения объектов в строку, словарь или что-то обычное для JSON
Допустим, у меня есть мой класс:
#libs
Как только появляется какой-либо класс мы терпим фиаско с ошибкой что-то вроде
TypeError: Object of type MyClass is not JSON serializableПричём не поддерживаются даже стандартные классы типа datetime или re.Pattern 😢. Повезло лишь некоторым классам, которые предназначены для хранения данных, например namedtuple или defaultdict.
Как сделать так, чтобы любой объект смог сериализоваться в JSON?
Обычный подход это создать свой класс-сериализатор, где и будет прописан алгоритм превращения объектов в строку, словарь или что-то обычное для JSON
Допустим, у меня есть мой класс:
class MyClass:Создадим сериализатор который умеет понимать такой тип объекта. Его задача: сохранить имя типа и данные инстанса чтобы потом можно было восстановить объект обратно. Для этого переопределим метод default(), котоырй по умолчанию выбрасывает ошибку когда объект не поддерживается.
def __init__(self):
self.x = 100
class MySerializer(json.JSONEncoder):Пробуем закодировать объект в JSON используя мой сериализатор
def default(self, obj):
if isinstance(obj, MyClass):
return {'type': MyClass.__name__,
'data': obj.__dict__}
return super().default(obj)
>>> mc = MyClass()Отлично, теперь json понимает мой тип! 😎
>>> json.dumps(mc, cls=MySerializer)
'{"type": "MyClass", "data": {"x": 100}}'
#libs
В прошлом посте мы научили JSON понимать новый тип данных. Но что если придётся записывать в JSON много разных неподдерживаемых типов?
Описывать для каждого отдельный
А если нам не известно что именно придётся сериализовать и нужно поддерживать в принципе всё что угодно? Ну хотя бы как-то записать объект чтобы не было ошибки и чтобы объект записался в JSON более менее информативно.
В этом случае можно вызвать стандартные способы репрезентации объекта. Самое простое это функция repr(). И тут уж как повезёт с тем, как именно разработчик позаботился о таком поведении его класса.
Более описательный метод, это закинуть объект в словарь с именем класса и данными инстанса.
В общем, возвращаясь к примеру из прошлого поста, мы просто убираем проверку конкретного типа
Скорее всего найдутся люди, которые осудят такой подход.
Может выбрасывать ошибку в случае отсутствия метода
#libs #tricks
Описывать для каждого отдельный
if isinstance()? А если нам не известно что именно придётся сериализовать и нужно поддерживать в принципе всё что угодно? Ну хотя бы как-то записать объект чтобы не было ошибки и чтобы объект записался в JSON более менее информативно.
В этом случае можно вызвать стандартные способы репрезентации объекта. Самое простое это функция repr(). И тут уж как повезёт с тем, как именно разработчик позаботился о таком поведении его класса.
Более описательный метод, это закинуть объект в словарь с именем класса и данными инстанса.
В общем, возвращаясь к примеру из прошлого поста, мы просто убираем проверку конкретного типа
class MySerializer(json.JSONEncoder):Функция vars() аналогична обращению к атрибуту
def default(self, obj):
return {'type': obj.__class__.__name__,
'data': vars(obj)}
__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)⚠️ PS: Не могу сказать что решение идеально. Такую неявную сериализацию можно сравнить с замалчиванием ошибок.
'{"type": "datetime", "data": "datetime.datetime(...)"}'
try:То есть в какой-то момент вы будете получать совершенно бесполезные данные и придётся искать где это происходит. Если не определён метод
do_something()
except:
pass
__repr__ то вы получите что-то вроде такого<__main__.MyClass object at 0x00000147CE48CBC8>Что с этим делать? Непонятно! 🤔
Скорее всего найдутся люди, которые осудят такой подход.
Может выбрасывать ошибку в случае отсутствия метода
__repr__ в классе?#libs #tricks