Важное сообщение. Есть функция стандартной библиотеки
Cегодня я узнала важное. Вот это:
Ответ на вопрос "почему" искать в математике, например, тут: https://en.wikipedia.org/wiki/Empty_set
Any statement that begins "for every element of ∅" is not making any substantive claim; it is a vacuous truth. This is often paraphrased as "everything is true of the elements of the empty set".
all() -- возвращает True, если все элементы переданного в него списка True. >>> all(['one', True, 1])
True
>>> all(['one', True, 0])
False
Cегодня я узнала важное. Вот это:
>>> all([])
True
all() возвращает True, если передать в него пустой список. Это важно, помните об этом, чтобы не получить зелёные тесты, когда в действительности ничего не работает. Ответ на вопрос "почему" искать в математике, например, тут: https://en.wikipedia.org/wiki/Empty_set
Any statement that begins "for every element of ∅" is not making any substantive claim; it is a vacuous truth. This is often paraphrased as "everything is true of the elements of the empty set".
Wikipedia
Empty set
set (in mathematics) containing no elements
У меня есть колонка данных. Например, несколько строк, которые я получила после выполнения запроса к базе. Теперь мне нужно эту колонку как-то быстро превратить в строку, разделённую запятыми. Было:
Должно стать:
Давайте пробовать. Есть утилита
Вот такой результат получится, если попросить заменить перенос строки на одинокую запятую:
Всё хорошо, но иногда нужно, чтобы обязательно после запятой был пробел. Пробуем добавить в tr пробел, tr его игнорирует:
Игнорирует потому, что
Попробуем другие (менее изящные, но рабочие) способы.
А, ещё обратите внимание, что при использовании tr сносится и последний знак переноса строки, так что строка ввода оказывается на строке предыдущего выхлопа:
Чтобы из колонки сделать красивую строку с разделителем в виде запятой и пробела и со знаком новой строки на конце, можно использовать:
Вариант 1.
Можно просто сохранить себе в сниппеты и использовать, когда надо (: А можно разобраться. Давайте разберёмся.
Вариант 2.
В конце концов канал у меня и про Python тоже, давайте использовать его силу. В этом проще разобраться и проще запомнить, чем прорву опций
В обоих случаях получим красивое желанное:
one
two
three
Должно стать:
one, two, three
Давайте пробовать. Есть утилита
tr для преобразования символов. Ей можно передать нашу колонку на вход и попросить заменить один символ на другой. tr отличный, маленький и удобный, но есть проблема, которую мне так и не удалось победить. Вот такой результат получится, если попросить заменить перенос строки на одинокую запятую:
~$ xsel | tr '\n' ','
one,two,three~$
Всё хорошо, но иногда нужно, чтобы обязательно после запятой был пробел. Пробуем добавить в tr пробел, tr его игнорирует:
~$ xsel | tr '\n' ', '
one,two,three~$
Игнорирует потому, что
tr мапит один символ на другой один символ. Нельзя заставить его заменить один символ двумя другими, он просто берёт первый из указанных. Попробуем другие (менее изящные, но рабочие) способы.
А, ещё обратите внимание, что при использовании tr сносится и последний знак переноса строки, так что строка ввода оказывается на строке предыдущего выхлопа:
one,two,three~$ -- видите ~$ тут? Некрасиво и не всегда удобно. Полечим в следующем примере. Чтобы из колонки сделать красивую строку с разделителем в виде запятой и пробела и со знаком новой строки на конце, можно использовать:
Вариант 1.
xsel | awk -vORS=", " '{ print $1 }' | sed 's/, $/\n/'Можно просто сохранить себе в сниппеты и использовать, когда надо (: А можно разобраться. Давайте разберёмся.
xsel -- напоминаю, это утилита, которая выводит в stdout то, что у вас в буфере обмена. awk -vORS=", " '{ print $1 }' -- вывести первую колонку входных данных, используя разделитель вида ", ".sed 's/, $/\n/' -- обрезать висящий на хвосте строки ", " и заменить его символом новой строки.Вариант 2.
В конце концов канал у меня и про Python тоже, давайте использовать его силу. В этом проще разобраться и проще запомнить, чем прорву опций
awk и последовательность слешей в sed (%xsel | python -c 'import sys; print(", ".join(r.strip() for r in sys.stdin))'В обоих случаях получим красивое желанное:
~$ xsel | awk -vORS=", " '{ print $1 }' | sed 's/, $/\n/'
one, two, three
~$ xsel | python -c 'import sys; print(", ".join(r.strip() for r in sys.stdin))'
one, two, threeКороткая заметка для тех, кто пользуется Ansible. Вывести все доступные переменные можно следующей командой:
ansible localhost, -m debug -a msg="{{ vars }}"У вас большой и сложный uWSGI конфиг? Разные секции, magic variables, if'ы и плейсхолдеры? Добавьте туда же опцию
show-config = true, чтобы при старте приложения в логах увидеть все опции, с которым приложение запускается.Написала невозможно длинный текст по итогам просмотра видео с CppCon, выложила на Yandex Zen, там даже можно оставить комментарии. Излагаю мысли о рефакторинге, техническом долге и тестах: https://zen.yandex.ru/media/id/5cf4dd82e84caf00ae7ac62b/spokoino-pro-refaktoring-i-negoduiusce-pro-tehnicheskii-dolg-5d11e89b687e2700ae48e6aa
Яндекс Дзен
Спокойно про рефакторинг и негодующе про технический долг
Мысли о докладе, где автор пытается ответить на вопрос: "Что делать, если вам достался в наследство гигантский проект?" -- рассказываю, с чем согласна, что классно, а что вызывает негодование и плевки.
Мне нужно проверить, что строка -- это число. Важно: обычно под задачей "проверить, что строка -- число" подразумевается "убедиться, что строка -- это натуральное десятичное число". То есть строка, будучи переданной в функцию
Можно в лоб:
А можно короче, воспользовавшись методом строки. Но каким? Есть три внешне похожих метода:
Чем они отличаются? Кого лучше использовать?
Для нашей задачи про "натуральное десятичное число" подходит только
Если строка валидируется, чтобы дальше конвертировать её с помощью
int(), корректно сконвертируется в объект типа int. Можно в лоб:
try:
number = int(my_string)
except ValueError:
print("Not a number")
А можно короче, воспользовавшись методом строки. Но каким? Есть три внешне похожих метода:
>>> '1'.isdecimal()
True
>>> '1'.isdigit()
True
>>> '1'.isnumeric()
True
Чем они отличаются? Кого лучше использовать?
isdecimal() вернет True только для подобных строк: "12345"
"12"
"98201"
isdigit() вернет True для всех предыдущих и чисел в верхнем регистре: "12345"
"1233³"
"³"
isnumeric() вернет True для всех предыдущих и дробей: "12345"
"½¼"
"½³"
"12345½"
Для нашей задачи про "натуральное десятичное число" подходит только
isdecimal(). Он вернёт True только в том случае, если переданная строка может быть использована в качестве аргумента функции int(). Например, если взять тройку в верхнем регистре и воспользоваться всеми тремя методами, то получим следующее: >>> '³'.isdecimal()
False
>>> '³'.isdigit()
True
>>> '³'.isnumeric()
True
isdigit() и isnumeric() возвращают True, и это поломает дальнейшую конвертацию с помощью int(): >>> int('³')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: invalid literal for int() with base 10: '³'Если строка валидируется, чтобы дальше конвертировать её с помощью
int(), то нужно использовать isdecimal().У вас есть репозиторий с большим проектом, над которым работает не один человек. В какой-то момент вы обнаруживаете, что одно из приложений, являющееся частью этого репозитория, не работает. Вы знаете коммит, на котором приложение точно работало, но не знаете, кто и сколько раз вносил изменения с тех пор. Как из потока ежедневных коммитов в самые разные части репозитория выделить изменения, касающиеся только этого файла, и посмотреть их?
Где commit -- это хеш коммита, на котором все работало, а file path -- относительный или абсолютный путь до интересующего вас файла. Например:
В выхлопе увидите все изменения, которые случились с файлом, начиная указанным коммитом.
И да, с директориями команда тоже работает.
git diff <commit>.. -- <file path>
Где commit -- это хеш коммита, на котором все работало, а file path -- относительный или абсолютный путь до интересующего вас файла. Например:
git diff 16ccf42.. -- py/kcarectl.pyВ выхлопе увидите все изменения, которые случились с файлом, начиная указанным коммитом.
И да, с директориями команда тоже работает.
Пингвитон
У меня есть колонка данных. Например, несколько строк, которые я получила после выполнения запроса к базе. Теперь мне нужно эту колонку как-то быстро превратить в строку, разделённую запятыми. Было: one two three Должно стать: one, two, three Давайте пробовать.…
Тут я рассказывала, как из колонки сделать одну строку, разделённую запятыми. Сейчас мне понадобилось каждую строчку оригинальной колонки ещё и обернуть кавычками, а потом собрать из этого одну строку, разделённую запятыми. Делать это вот так (следите за кавычками и их экранированием, они такие не просто так, другие варианты не работают):
Единственный минус. Отсутствует кавычка в начале и в конце строки -- их надо доставить руками. Но это проще, чем оборачивать кавычками и расставлять запятые у пары десятков элементов (:
xsel | python -c "import sys; print('\', \''.join(r.strip() for r in sys.stdin))"Единственный минус. Отсутствует кавычка в начале и в конце строки -- их надо доставить руками. Но это проще, чем оборачивать кавычками и расставлять запятые у пары десятков элементов (:
PDF → PNG
Сконвертировать PDF файл в PNG быстро и одной командой без загрузки файлов куда-то в интернет:
Обратите внимание, что имя выходного файла нужно указать без расширения. Если будет указано name.png, то в финале именем файла окажется name.png-1.png
Это ок, если в PDF несколько страниц. Будет создано соответствующее количество PNG с именами вида: name-1.png, name-2.png и т.д.
Если вам нужно сконвертировать несколько PDF из одной директории, то, чтобы сделать это сразу одной командой, а не по одному файлу, можно воспользоваться xargs:
По итогам этой команды у вас будут PNG со страшненькими именами вида file.pdf-1.png, но обычно это не проблема + есть точная привязка "из какого PDF этот PNG" + пачка PDF сконвертирована быстро и без лишних усилий.
PNG → PDF
Обратный процесс. Собрать из нескольких PNG файлов, лежащих в одной директории, один PDF:
Сконвертировать PDF файл в PNG быстро и одной командой без загрузки файлов куда-то в интернет:
pdftoppm -png /tmp/health/file.pdf /tmp/health/new-file-name-without-extension
Обратите внимание, что имя выходного файла нужно указать без расширения. Если будет указано name.png, то в финале именем файла окажется name.png-1.png
Это ок, если в PDF несколько страниц. Будет создано соответствующее количество PNG с именами вида: name-1.png, name-2.png и т.д.
Если вам нужно сконвертировать несколько PDF из одной директории, то, чтобы сделать это сразу одной командой, а не по одному файлу, можно воспользоваться xargs:
ls /tmp/health/ | xargs -n1 --replace=^ echo "pdftoppm -png /tmp/health/^ /tmp/health/^" | bash
По итогам этой команды у вас будут PNG со страшненькими именами вида file.pdf-1.png, но обычно это не проблема + есть точная привязка "из какого PDF этот PNG" + пачка PDF сконвертирована быстро и без лишних усилий.
PNG → PDF
Обратный процесс. Собрать из нескольких PNG файлов, лежащих в одной директории, один PDF:
gm convert /tmp/pictures/*.png /tmp/result.pdf
У вас заканчивается место на сервере и непонятно, кто его отжал. Вы посмотрели логи и очевидные места, куда пишут приложения, но везде всё законно и умеренно. Где мои 20 ГБ места? На помощь приходит это:
Не выйдет за пределы файловой системы (
du -xmt 100M / | sort -n
Не выйдет за пределы файловой системы (
-x ), просмотрит от корня ( / ), покажет все файлы больше 100 МБ ( -t 100M ), покажет размер в МБ ( -m ), отстортирует в порядке возрастания ( | sort -n ).Примерно в 2012 году мне попался в интернетах кроссворд с регэкспами. Я его распечатала, решала на бумажке с карандашом несколько дней и была в восторге. После пыталась найти ещё, но успешна не была. А сейчас спустя годы попалось вот это: https://regexcrossword.com -- и это офигенно. Там есть туториал для начинающих, который состоит из простейших конструкций; кроссворды начинаются с простых и маленьких, а потом усложняются. А в конце вас ждёт тот самый монстр, что я решала в 2012. Отличная штука, чтобы разобраться в регэкспах, запомнить их и получить удовольствие.
Regex Crossword
A crossword puzzle game using regular expressions. Earn achievements completing puzzle challenges. Easy tutorials for people new to regular expressions.
Иногда нужно вывести весь файл, кроме первой строки. Например, в файле есть несколько колонок, которые вы хотите как-то проанализировать и обработать, а названия колонок мешаются. Вывести содержимое файла без первой строки можно так:
Эта запись значит "вывести все строки файла, начиная со второй". Подставить можно любое число в зависимости от того, сколько первых строк файла вы хотите исключить из вывода.
А вот такая запись (без `+`) приведёт к выводу последних двух строк файла:
tail -n+2 /tmp/servers.txt
Эта запись значит "вывести все строки файла, начиная со второй". Подставить можно любое число в зависимости от того, сколько первых строк файла вы хотите исключить из вывода.
А вот такая запись (без `+`) приведёт к выводу последних двух строк файла:
tail -n2 /tmp/servers.txt
Часто нужно скопировать из терминала выхлоп какой-то команды. Например, я регулярно использую
-- скопирует результаты команды
realpath, чтобы вывести оригинальный путь вместо симлинка или абсолютный путь вместо относительного, а затем мне нужно этот полученный путь скопировать и куда-то вставить -- в другой терминал, редактор, мессенджер. Раньше я медленно и печально выделяла, а потом нажимала ctrl+shift+c. Иногда не с первого раза попадаешь в выделение, иногда в Gnome почему-то не с первого раза работает копирование. Печаль и раздражение. Теперь использую xsel -- это быстро и очень удобно. realpath tmp/ | xsel -b
-- скопирует результаты команды
realpath tmp/ в буфер обмена. Могу нажать ctrl+v и вставить полученный результат прямо в это сообщение: /home/lauvenhelz/tmpНайти и удалить все .pyc файлы в директории и поддиректориях:
Однако, сначала лучше запустить эту же команду без
И да, очевидно, расширение можно подставить любое (:
Кроме того, можно использовать не только для файлов (
find . -name "*.pyc" -type f -delete
Однако, сначала лучше запустить эту же команду без
-delete, чтобы убедиться, что в списке только то, что вы действительно хотите удалить: find . -name "*.pyc" -type f
И да, очевидно, расширение можно подставить любое (:
Кроме того, можно использовать не только для файлов (
-type f ), но и для имён директорий ( -type d ) или симлинков ( -type l ), больше возможных типов см. в man find (на самом деле, тип в команде можно вообще не использовать, но, чтобы случайно не удалить лишнее, лучше указывать).
Пингвитон
У меня есть таймстемп, например, 1549886527, и я хочу быстро сконвертировать его во что-то человекочитаемое. Использую date: ~$ date -u -d @1549886527 Mon Feb 11 12:02:07 UTC 2019 На всякий случай: таймстемп -- это seconds since the epoch, а epoch -- это…
Тут ^^^ рассказывала, как сконвертировать таймстемп в человекочитаемое время из консоли. А вот так можно его в консоли сгенерировать:
Раньше для этого запускала Python, импортировала
Однако, консольный date выводит только секунды. Если нужна большая точность, то всё равно придётся обратиться к Python:
~$ date +%s
1591195563
Раньше для этого запускала Python, импортировала
time и выполняла time.time(), теперь можно проще и быстрее. Однако, консольный date выводит только секунды. Если нужна большая точность, то всё равно придётся обратиться к Python:
>>> import time
>>> time.time()
1591195700.0724027
Читатель подсказывает, что date тоже умеет:
Я, правда, пока не решила, что мне проще-удобнее -- вводить это или всё же сходить в питонячью консоль и заимпортить time (:
~$ date +%s.%N
1591202943.155504934
Я, правда, пока не решила, что мне проще-удобнее -- вводить это или всё же сходить в питонячью консоль и заимпортить time (:
У меня есть большой файл, который я хочу передать другому человеку. Если я нахожусь с ним в одной сети, мне не понадобится медленно и печально выкладывать его на dropbox, google drive, отправлять в Телеграм или по почте. Я могу с помощью Python в одну команду поднять HTTP сервер, сделав доступной директорию, где лежит файл, и другой человек скачает его напрямую.
Вот моя директория, где лежит файл, который я хочу сделать доступным для скачивания:
В этой директории запускаю следующее:
(это для python3; для python2 замените
Всё. Сервер поднят. По дефолту поднимается на 8000 порту. Если дефолтное поведение забылось, то порт любезно пишется в первой же строке после запуска сервиса (:
Осталось узнать свой ip внутри сети и отдать ссылку другому человеку. Запускаем
Моя ссылка, чтобы расшарить файл: http://192.168.1.11:8000/ -- человек из одной со мной сети может скачать по ней файл. Не забудьте остановить сервис (нажать ctl+c), когда файл будет скачан.
Вот моя директория, где лежит файл, который я хочу сделать доступным для скачивания:
~/tmp/dir-to-share$ ls -la
total 188088
drwxr-xr-x 2 lauvenhelz lauvenhelz 4096 Jun 9 21:06 .
drwxr-xr-x 6 lauvenhelz lauvenhelz 4096 Jun 9 21:05 ..
-rw-r--r-- 1 lauvenhelz lauvenhelz 192587776 Jun 9 21:06 wetches.db
В этой директории запускаю следующее:
~/tmp/dir-to-share$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
(это для python3; для python2 замените
http.server на SimpleHTTPServer )Всё. Сервер поднят. По дефолту поднимается на 8000 порту. Если дефолтное поведение забылось, то порт любезно пишется в первой же строке после запуска сервиса (:
Осталось узнать свой ip внутри сети и отдать ссылку другому человеку. Запускаем
ip r, смотрим src для маршрута по умолчанию: ~$ ip r | head -1
default via 192.168.1.1 dev wlp2s0 proto dhcp src 192.168.1.11 metric 302
Моя ссылка, чтобы расшарить файл: http://192.168.1.11:8000/ -- человек из одной со мной сети может скачать по ней файл. Не забудьте остановить сервис (нажать ctl+c), когда файл будет скачан.
Полагаю, все однажды видели или использовали вот эту конструкцию:
Это обычный способ реализации логики вида "нужно поймать эксепшен конкретного типа, проигнорировать его и продолжить работу". В стандартной библиотеке есть более лаконичный способ:
Это контекстный менеджер
Документация стандартной библиотеки напоминает, что "как и с любым другим механизмом, полностью подавляющим эксепшены, этот контекстный менеджер следует использоваться только в конкретных специфических случаях, когда молчаливое продолжение работы программы после возникновения эксепшена точно является правильным поведением".
В общем, не глотайте эксепшены без повода, процесс дебага может стать очень болезненным (:
Хороший пример, когда этот контекстный менеджер удобно использовать, -- удаление файла. Я хочу почистить старые файлы перед запуском программы, но может быть так, что это первый запуск и старых файлов ещё не накопилось. Делаю так:
Этот контекстный менеджер доступен, начиная с Python 3.4.
Если вы используете более старые версии Питона, то вы можете добавить в проект собственную реализацию, которая будет выглядеть так:
try:
<какой-то код>
except <какой-то тип эксепшена>:
pass
Это обычный способ реализации логики вида "нужно поймать эксепшен конкретного типа, проигнорировать его и продолжить работу". В стандартной библиотеке есть более лаконичный способ:
from contextlib import suppress
with suppress(<какой-то тип эксепшена>):
<какой-то код>
Это контекстный менеджер
suppress, который импортируется из модуля contextlib. Ему передаётся тип эксепшена (или типы, можно несколько, да), который необходимо "проглотить", дальше в теле with пишем код, который эти эксепшены может выкинуть. Если эксепшен возникнет, он будет пойман и проигнорирован, а управление перейдёт к первой строчке после блока with. Документация стандартной библиотеки напоминает, что "как и с любым другим механизмом, полностью подавляющим эксепшены, этот контекстный менеджер следует использоваться только в конкретных специфических случаях, когда молчаливое продолжение работы программы после возникновения эксепшена точно является правильным поведением".
В общем, не глотайте эксепшены без повода, процесс дебага может стать очень болезненным (:
Хороший пример, когда этот контекстный менеджер удобно использовать, -- удаление файла. Я хочу почистить старые файлы перед запуском программы, но может быть так, что это первый запуск и старых файлов ещё не накопилось. Делаю так:
with suppress(FileNotFoundError):
os.remove('my-file.txt')
Этот контекстный менеджер доступен, начиная с Python 3.4.
Если вы используете более старые версии Питона, то вы можете добавить в проект собственную реализацию, которая будет выглядеть так:
from contextlib import contextmanager
@contextmanager
def suppress(*exceptions):
try:
yield
except exceptions:
pass
Я тут писала письмо пользователям, где доносила мысль, что для влючения некоторого функционала нужно добавить переменную в конфиг. И для облегчения пользовательской жизни (чтобы им не нужно было открывать файл, вставлять туда данные, сохранять и закрывать файл), решила использовать
Команда сначала выглядела как-то так:
Предполагается, что команда должна добавить в конец файла новую строчку с переменной. Так вот.
Не надо так.
Что может пойти не так? В local.py уже могут быть данные. Но мы же используем >> , это как раз про добавление в конец файла, разве нет? Да, но кто сказал, что добавление данных в конец файла == добавление данных на новую строку? Чтобы новые данные встали на новую строку, предыдущая строка должна заканчиваться \n. Где гарантия, что при предыдущих изменениях файла этот \n там появился? А вдруг файл выглядит вот так?
Если для такого файла я воспользуюсь командой из начала, то в итоге получу:
И конфиг, очевидно, не будет работать. Что делать? Вставлять \n самостоятельно перед своими данными. Однако, если просто добавить \n в команду, снова получится не то:
Чтобы всё это заработало, echo нужно передать опцию
Теперь конфиг в безопасности.
echo. Команда сначала выглядела как-то так:
echo 'BOO = True' >> /my/project/local.py
Предполагается, что команда должна добавить в конец файла новую строчку с переменной. Так вот.
Не надо так.
Что может пойти не так? В local.py уже могут быть данные. Но мы же используем >> , это как раз про добавление в конец файла, разве нет? Да, но кто сказал, что добавление данных в конец файла == добавление данных на новую строку? Чтобы новые данные встали на новую строку, предыдущая строка должна заканчиваться \n. Где гарантия, что при предыдущих изменениях файла этот \n там появился? А вдруг файл выглядит вот так?
~$ cat /tmp/local.py
FOO = False~$
Если для такого файла я воспользуюсь командой из начала, то в итоге получу:
~$ echo 'BOO = True' >> /tmp/local.py
~$ cat /tmp/local.py
FOO = FalseBOO = True
И конфиг, очевидно, не будет работать. Что делать? Вставлять \n самостоятельно перед своими данными. Однако, если просто добавить \n в команду, снова получится не то:
~$ echo '\nBOO = True' >> /tmp/local.py
~$ cat /tmp/local.py
FOO = False\nBOO = True
Чтобы всё это заработало, echo нужно передать опцию
-e (enable interpretation of backslash escapes): ~$ echo -e '\nBOO = True' >> /tmp/local.py
~$ cat /tmp/local.py
FOO = False
BOO = True
Теперь конфиг в безопасности.