Получи случайную криптовалюту за регистрацию!

Python Заметки

Логотип телеграм канала @pythonotes — Python Заметки P
Логотип телеграм канала @pythonotes — Python Заметки
Адрес канала: @pythonotes
Категории: Технологии
Язык: Русский
Количество подписчиков: 2.71K
Описание канала:

Интересные заметки и обучающие материалы по Python
Контакт: @paulwinex
Хештеги для поиска:
#tricks
#libs
#pep
#basic
#regex
#qt
#django
#2to3
#source
#offtop

Рейтинги и Отзывы

3.00

3 отзыва

Оценить канал pythonotes и оставить отзыв — могут только зарегестрированные пользователи. Все отзывы проходят модерацию.

5 звезд

0

4 звезд

2

3 звезд

0

2 звезд

0

1 звезд

1


Последние сообщения 2

2022-01-17 12:02:04 Синтаксис f-string позволяет использовать в строке символ обратного слеша "\" но не позволяет использовать его внутри фигурных скобок.

>>> lines = ['line1','line2']
>>> print(f'ITEMS: \n {"\n".join(lines)}')
SyntaxError: f-string expression part cannot include a backslash

Решения:

вынести этот символ за скобки

n = '\n'
print(f'ITEMS: \n{n.join(lines)}')

заменить его на другое представление, например создавать нужный символ с помощью функции chr()

print(f'ITEMS: \n{chr(10).join(lines)}')

Чтобы узнать код символа следует использовать функцию ord()

>>> ord('\n')
10

А с юникодом не выйдет, там тоже слеш

>>> print(f'ITEMS: \n{"\u000a".join(lines)}')
SyntaxError: f-string expression part cannot include a backslash

#tricks
596 views09:02
Открыть/Комментировать
2022-01-14 12:02:04 Ещё пара триков с форматированием!

Порядки больших чисел можно разделять запятой (и только запятой)

>>> '{:,}'.format(1_231_312)
'1,231,312'

Если оставить пробел в форматировании float после ":" то он добавится в строку

>>> '{: .2f}'.format(15)
' 15.00'

Но если подать отрицательное число, то знак минуса займёт этот пробел

>>> '{: .2f}'.format(-15)
'-15.00'

Удобно для формирования строк одинаковой длины независимо от знака числа.

#tricks
557 views09:02
Открыть/Комментировать
2022-01-12 12:01:22
При форматировании числа в строку можно задать паддинг - заполнение нулями до нужной длины.

>>> '{}_v{:05d}'.format('name', 125)
'name_v00125'

А что если паддинг тоже задан переменной? В таком случае мы можем добавить форматирование этой переменной внутри формата первой. Порядок переменных следует указывать в порядке появление открывающейся скобки.

>>> '{}_v{:0{}d}'.format('name', 125, 5)
'name_v00125'

Или вот так
>>> '{}_v:{:{:02d}{}}'.format('name', 125, 5, 'd')
'name_v00125'

А можно глубже?

К сожалению (а может к счастью), уровень вложенности ограничен двумя. То есть, форматирование внутри форматирования внутри форматирования это ...

ValueError: Max string recursion exceeded

Но никто не запрещает использовать один тип форматирования внутри другого.

(Пример странный, но зато рабочий)

>>> ('{}_v{:{:%0.2dd}{}}' % 2).format('name', 125, 5, 'd')
'name_v00125'

С f-string можно использовать 4 уровня, по количеству доступных типов кавычек. Можно, но не нужно

f'''{f"""{f'{f"{x}"}'}"""}'''


#tricks
560 viewsedited  09:01
Открыть/Комментировать
2022-01-05 10:00:01
По статистике 7 часов ежедневно мы проводим онлайн, половину тратим впустую. Станьте исключением и присоединяйтесь к каналу
AI анализ и развитие, в котором вы найдете знания о:

• машинном обучении (data science, нейросетях);
• секретах продуктивности и личного развития;
• методиках анализа и критической оценки информации;
• а на десерт - интересные факты и юмор.

Погрузитесь в мир аналитики и прокачайте навыки 21 века вместе со специалистом,
который сделает ваше путешествие понятным и увлекательным.

Подписывайтесь на @ai_analysis
435 views07:00
Открыть/Комментировать
2022-01-01 11:59:59
Всех c 2k22!

А еще сегодня каналу @pythonotes исполнилось годика
526 views08:59
Открыть/Комментировать
2021-12-27 12:00:53 Наверняка вы замечали, что в Python есть удобная функция для получения переменной окружения

os.getenv(NAME)

И её "сестра" для создания или изменения переменных окружения

os.putenv(NAME, VALUE)

Но почему-то putenv() не работает как должно. Энвайромент не обновляется!

os.putenv('MYVAR', '1')
print(os.getenv('MYVAR'))

... и ничего

Почему так?

На самом деле энвайромент обновляется, но это значение не добавляется в словарь os.environ.
Откройте исходник функции os.getenv(). Это просто шорткат для os.environ.get()
В то время как putenv() это built-in С-функция.

Словарь os.environ (или точней класс из MutableMapping) создаётся из энвайромента в момент инициализации. Функция putenv() самостоятельно его не изменяет.

В тоже время, когда вы создаёте или изменяете ключ в os.environ, автоматически вызывается putenv() в методе __setitem__().

То есть, технически putenv() всё делает верно, но в os.environ это не отражается. Можно проверить так:

>>> os.putenv('MYVAR', '123')
>>> os.system('python -c "import os;print(os.getenv(\'MYVAR\'))"')
123

Я объявил переменную в текущем процессе и вызвал дочерний процесс, который её унаследовал и получил в составе os.environ.

Аналогично при удалении переменной вызывается еще одна built-in функция unsetenv(), удаляющая переменную из системы.

Итого

Удобней всего явно обновлять переменные через os.environ

Есть способ неявно создать/удалить переменную через putenv/unsetenv, что не повлияет на os.environ но изменит энвайромент и передаст изменения сабпроцессам. Но так лучше не делать!

os.environ это просто обертка для built-in функций putenv() и unsetenv().

#basic
689 views09:00
Открыть/Комментировать
2021-12-20 12:01:19 Заметка от читателя @nencoru

Как смержить несколько файлов с отсортированными строками в один файл, тоже отсортированный?

Исходный файл:

в одной строке находится одна запись
запись содержит предсказуемое поле с некими данными для сортировки. Например, логи с указанием времени.
все записи в файле отсортированы по этому полю

Задача:

Смержить несколько таких файлов так, чтобы в финальном файле все записи были также отсортированы.

Решение 1:

Предположим, у меня JSON-логи

files = ['file1.jsonl', 'file2.jsonl', ...]

data = []
for file in files:
with open(file) as f:
data.expand(
f.readlines()
)
data.sort(key=lambda line: json.loads(line)['timestamp'])
with open('merged.jsonl', 'w') as f:
f.writelines(data)

Усложним задачу — размер каждого файла 5Gb

Это означает, что вам потребуется оперативной памяти 5*len(files) Gb.
И уже не каждый компьютер сможет смержить 3-4 таких файла. А если их 100?

fileinput может помочь написать более красивый код, но с памятью не поможет.

Решение 2:

Можно использовать готовую функцию heapq.merge() из стандартного модуля heapq!

Heap - это бинарное дерево, где каждый родительский элемент в дереве имеет значение меньшее чем дочерний.

То есть, по умолчанию все элементы как-либо отсортированы.

from heapq import merge
items = [
[3,2,6],
[1,5,4]
]
print(list(merge(*items)))
# [1, 2, 3, 4, 5, 6]

А учитывая, что merge это генератор, умеет работать с файлами и ему можно передать функцию для сортировки, он отлично подойдет для нашей задачи, так как полной загрузки в память не происходит!

И тут вы спросите: что за магия?
Тоже самое только без расхода памяти? Волшебный генератор всех спасёт?

Нет, за всё приходится платить. В случае с heapq весь процесс драматически замедлится.
Но задача будет выполнена!

Сделал для вас синтетический пример для генерации и мержа подобных файлов
Код смотреть здесь

make_logs() генерит 30 файлов по 50Mb для теста
merge_list() мержит файлы через простой список
merge_heapq() мержит файлы через heapq
memory_profiler считает используемую память (нужно установить модуль)
также есть замер времени

Кому лениво там же смотрите мои тесты:

1.5Gb и 19.5с против 19Mb и 2м 43с
памяти в 80 раз меньше, но времени в 8 раз больше

ВАЖНО
для чистоты эксперимента запускать следует из консоли и по одному тесту на процесс. То есть закоментили второй, запустили первый, закоментили первый, запустили второй.

#tricks
478 views09:01
Открыть/Комментировать
2021-12-07 15:16:02
Состоялся релиз DJANGO 4.0

Полный список изменений читаем здесь

#django
605 views12:16
Открыть/Комментировать
2021-12-03 12:01:25

Поздравляю всех 3Dшников с проф-праздником!

PS. А еще на канале юбилейный пост под номером 300!

#offtop
855 views09:01
Открыть/Комментировать
2021-12-01 12:01:21 Если часто работаете с архивами, то наверняка знакомы с модулем zipfile.
Нет ничего сложного добавить директорию в архив.

import zipfile
from pathlib import Path

dir_name = '~/input_files'
zip_name = '~/archive.zip'

with zipfile.ZipFile(zip_name, 'w') as zip:
for file in Path(dir_name).glob('**/*'):
zip.write(file, file.relative_to(dir_name).as_posix())

Довольно немногословно. Но можно короче! В модуле shutil уже есть готовый метод

dir_name = '~/input_files'
zip_name = '~/archive'
aip_file = shutil.make_archive(zip_name, 'zip', root_dir=dir_name)

А что насчет распаковки?
Здесь проще, даже с zipfile это одна строка

zip_file = '~/archive.zip'
out_dir = '~/out_dir'
zipfile.ZipFile(zip_file).extractall(out_dir)

Ну и тем более в shutil

shutil.unpack_archive(zip_file, out_dir)

В примерах не делается expanduser для краткости

В данном случае функция из shutil более универсальна, так как второй аргумент format задаёт алгоритм сжатия, от чего зависит выбор библиотеки. Если написать формат tar, то вместо zipfile будет использоваться tarfile.
Поддерживаются форматы zip, tar, gztar, bztar, xztar. Но только если на текущем хосте доступны соответствующие библиотеки.

Из недостатков можно назвать невозможность запаковать просто один файл. Источником может быть только директория. Проблема легко решается, но всё же.

Интересный момент. При наличии в Python2 функции shutil.make_archive() там отсутствует shutil.unpack_archive(). А появился он только в 3.7! Не очень понятно почему, но это еще один повод переходить на Python3

#libs #tricks
620 viewsedited  09:01
Открыть/Комментировать