uk
Feedback
Python Заметки

Python Заметки

Відкрити в Telegram

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

Показати більше
2 373
Підписники
Немає даних24 години
-17 днів
-1130 день
Архів дописів
Релиз Python 3.10 случился! Все быстро побежали использовать новые type hints, pattern matching и всё такое😁 А между тем, на
Релиз Python 3.10 случился! Все быстро побежали использовать новые type hints, pattern matching и всё такое😁 А между тем, на днях вышел Qt6.2. Наконец-то портировали такие модули как QtBluetooth, QtMultimedia, QtWebEngine, QtWebView и другие полезняхи. Если вы этого ждали, то пора действовать! PySide6 тоже подтянулся по версии. #qt #libs

Python 3.10 Release Stream — with Pablo Galindo Стрим, посвящённый релизу Python 3.10! ➡️ https://www.youtube.com/watch?v=AHT
Python 3.10 Release Stream — with Pablo Galindo Стрим, посвящённый релизу Python 3.10! ➡️ https://www.youtube.com/watch?v=AHT2l3hcIJg

Особенно внимательно за закрытием файлов нужно следить в задачах где вы обрабатываете много файлов. Операционная система имеет ограничение на количество открытых файлов процессом и вы быстро можете дойти до этого предела.
import os
lst = []
for i in range(100000):
    lst.append(open(os.devnull, 'w'))

OSError: [Errno 24] Too many open files: 'nul'

Чтобы узнать лимит на Linux вызовите команду
ulimit -n

(Полный список лимитов ulimit -a) Изменить лимит можно командой
ulimit -n 2048

На Windows можно это сделать с помощью кода
>>> import ctypes
>>> ctypes.windll.msvcrt._getmaxstdio()
512
>>> ctypes.windll.msvcrt._setmaxstdio(2048)
2048

➡️ Отсюда вывод: Всегда явно закрывайте файлы! Иначе можете упереться в ограничения системы. #tricks

В одном из прошлых постов был вопрос в комментариях по поводу закрытия файла. Тогда я бегло пояснил что Python сам собирает мусор в памяти. Давайте пройдемся по этому вопросу более внимательно. Когда вы открываете файл не сохраняя его в переменную, на самом деле файл остаётся открытым. Ссылка на него теряется где-то в памяти пока сборщик мусора не доберется до него. Давайте сделаем несколько экспериментов. Откроем файл не сохраняя его в переменную и поищем этот объект в памяти. Для поиска будем использовать вот такую функцию
def check_file():
  import io, gc
  for obj in gc.get_objects():
    if isinstance(obj, io.TextIOWrapper) and \
      obj.name == 'testfile':
      print('File', obj.name, 'is closed:', obj.closed)
        return 
  print('Not found')

Тест первый: файл, открытый в функции
def func():
  open('testfile', 'w')

Вызываем функцию и проверяем наличие файла
>>> func()
>>> check_file()
Not found

Здесь всё предсказуемо. Все локальные переменные функции удаляются когда функция завершится. А при удалении файл закрывается. Тест второй: теперь откроем файл просто в коде, можно в интерактивной консоли.
open('testfile', 'w')

И снова он не сохранён в переменную, а значит будет удалён сборщиком мусора. Проверим:
>>> check_file()
File testfile is closed: False

Бывалые сразу же скажут, что файл улетел в переменную "_", и будут правы!
>>> print(_)
<_io.TextIOWrapper name='testfile' mode='w' encoding='UTF-8'>

Давайте обнулим её, можно просто распечатать любое число в консоли
>>> 123
>>> print(_)
123

Проверим
>>> check_file()
Not found

➡️ Отсюда вывод: Всегда явно закрывайте файлы! Не рассчитывайте что кто-то (gc) сделает это за вас. _________________ Мои примеры порой содержат оптимизации в угоду краткости но в ущерб правильности. #tricks

Как легко отсортировать объект по нескольким ключам? Перед вами стоит задача сделать крутой фильтр для сайта по продаже подер
Как легко отсортировать объект по нескольким ключам? Перед вами стоит задача сделать крутой фильтр для сайта по продаже подержанных автомобилей. Для решения этой задачи часто требуется сортировать списки Самым оптимальным способом будет решение через operator.itemgetter() 👉 Разбор этой задачи с примерами Еще больше крутых фишек ➡️

Наверняка вы знаете что такое перегруженная функция в С++. Если нет, то всё просто. В С++ можно создать несколько функций с одинаковым названием но разными типами аргументов. И это будут разные функции. Во время вызова функции будет выбрана та её версия которая подходит по типам аргументов. Такая конструкция называется параметрический полиморфизм. Это удобно, когда мы точно не знаем какого типа прилетит аргумент и хотим обработать разные ситуации. Как мы это поведение можем повторить в Python? Обычно через проверку типов.
def func(value):
  if isinstance(value, int):
    return func_int(value)
  elif isinstance(value, str):
    return func_str(value)
  else:
    raise NotImplementedError

Не очень красиво 🧐 Начиная с версии 3.4 в Python добавили способ делать "перегруженные" функции более элегантно. Это декоратор singledispatch. Создадим нашу исходную функцию которая по умолчанию выбрасывает ошибку.
@singledispatch
def func(value)
  raise NotImplementedError

Декоратор добавил для объекта func новую функцию register() с помощью которой можем регистрировать перегруженные функции. В качестве аргумента указывайте тип который данная функция обрабатывает
@func.register(int)
def func_int(x):
    print("INT:", x)

Вместо указания типа в аргументах можно использовать аннотации аргумента функции
@func.register
def func_str(x: str):
    print("STR:", x)

Если не указать тип одним из этих способов то получите ошибку TypeError. Имя новой функции не имеет значения, часто её называют просто "_"
@func.register(list)
def _(x):
    print("LIST", x)

Если одна функция должна обработать несколько типов, то просто наслаиваем декоратор
@func.register(float)
@func.register(Decimal)
def _(x):
    print(f'{type(x).__name__.upper()}:', x)

Теперь у нас есть 5 отдельных функций которые вызываются в зависимости от типа передаваемого аргумента. При этом всё выглядит логично и компактно. Обработка каждого случая находится в своей отдельной функции!
>>> func(1)
INT: 1
>>> func('Python')
STR: Python
>>> func(1.2)
FLOAT: 1.2
>>> func({})
NotImplementedError

------ ◽️ Данный способ работает только с первым аргументом. Все остальные аргументы будут переданы как есть и не участвуют в выборе нужной функции. ◽️ В версии 3.8 доступен декоратор singledispatchmethod с таким же функционалом но для методов класса. #tricks #libs

Как зарабатывать на верстке в 2021? Прежде всего — сделать свое портфолио! Для этого вам понадобятся уникальные качественные
Как зарабатывать на верстке в 2021? Прежде всего — сделать свое портфолио! Для этого вам понадобятся уникальные качественные макеты. На канале Макеты для верстки || Vollex собраны действительно редкие и красивые шаблоны сайтов, которые заказчики присылают верстальщику. Учитесь, практикуйтесь, пополняйте свое портфолио настоящими работами вместе с @uniquetemplates!

Какой тип данных выбрать для оптимального хранения информации? Имеется в виду объем занимаемой памяти. Можем создать 4 разных варианта объектов и сравнить сколько они занимают оперативки. Будем создавать простой класс, класс со слотами, именованный кортеж и словарь. from collections import namedtuple from sys import getsizeof NT = namedtuple('NT', 'v1 v2 v3') class CLASS: def init(self, x1, x2, x3): self.v1 = x1 self.v2 = x2 self.v3 = x3 class SLOTS: slots = ['x1', 'x2', 'x3'] def init(self, x1, x2, x3): self.x1 = x1 self.x2 = x2 self.x3 = x3 d = dict(x1=1, x2=2, x3=3) c = CLASS(1, 2, 3) s = SLOTS(1, 2, 3) t = NT(1, 2, 3) Теперь распечатаем что там по памяти print(' CLS\t\tSLT\t\tDCT\t\tTPL') print(f'System: {getsizeof(c)}\t\t' f'{getsizeof(s)}\t\t' f'{getsizeof(d)}\t\t' f'{getsizeof(t)}' ) CLS SLT DCT TPL System: 48 56 232 64 Хм, в этой статистике обычный класс самый экономный! Но что-то здесь не так. Неужели он экономичней класса со слотами, который рассчитан на скорость и оптимизацию? Дело в том, что функция sys.getsizeof() показывает не совсем то что мы ожидаем. Она берет результат метода __sizeof__ у объекта и добавляет кое-чего от gc. __sizeof__ возвращает размер, занимаемый данными. Но не учитывает размер обвязки этих данных. А еще он не гарантирует точность размера типов для third-party расширений. Для точного измерения размера лучше использовать модуль pympler. Помимо данных он считает сколько места занимает вся структура классов и другая обвязка объекта. from pympler import asizeof print(f'Pympler: {asizeof.asizeof(c)}\t\t' f'{asizeof.asizeof(s)}\t\t' f'{asizeof.asizeof(d)}\t\t' f'{asizeof.asizeof(t)}' ) CLS SLT DCT TPL Pympler: 416 152 496 160 И вот тут класс со слотами оказывается самым оптимальным решением! И это правильно. А словарь в этом тесте оказался самый расточительный. #tricks #libs

Что-то вы гоните насчет "привычного вида формата 755 и 644". Я вот вообще не понял что это! 😳 Действительно, что означают цифры которые мы получили в прошлом посте? Это кодировка, заключающая в себе режимы доступа к файлу. Подробней можно почитать в статье про chmod. Там можно увидеть альтернативное обозначение того же самого с помощью символов r w x, что значит чтение, запись, исполнение. Чтобы преобразовать восьмеричное число в такое обозначение в Python есть готовая функция >>> stat.filemode(0o755) '?rwxr-xr-x' Мы видим 3 группы по 3 символа, дающие 3 типа доступа для 3 типов юзеров. А что за знак вопроса в начале? Давайте передадим в эту функцию необрезанное значение от os.stat >>> stat.filemode(os.stat(path).st_mode) 'drwxr-xr-x' Это данные, которые мы безжалостно обрезали в прошлый раз😼 Первый символ обозначает тип объекта. Это может быть файл (-), директория (d) или симлинк (l). Вот простая схема данной кодировки [1][3][3][3] │ │ │ │ │ │ │ └──> Others Permissions │ │ └─────> Group Permissions │ └────────> Owner Permissions └───────────> File Type (разверните экран если вы с телефона) Если вы попробуете получить пермишены для симлинка то получите пермишены для файла >>> path = '.venv/bin/python3' >>> stat.filemode(os.stat(path).st_mode) '-rwxr-xr-x' Чтобы получить свойства именно симлинка, нужно это явно указать >>> stat.filemode(os.stat(path, follow_symlinks=False).st_mode) 'lrwxrwxrwx' #tricks #basic

Как получить значение прав доступа к файлу в виде привычного формата записи 755 или 644? В целом, способ вот такой:
>>> path = '...'
>>> print(oct(os.stat(path).st_mode & 0o777).split('o')[-1])
'755'

Теперь разберёмся что всё это значит. До того как мы сделали split() строка была следующего вида: 0o755 Что это за тип данных? Это тип int в восмиричной системе исчисления. Для преобразования в такой формат есть builtin функция oct()
>>> oct(493)
'0o755'

Как видите, результат возвращается виде строки. Но это не мешает нам создавать переменные синтаксисом восьмеричных чисел, то есть с префиксом 0o. Хотя, при распечатке мы всё равно получим int.
>>> x = 0o777
>>> print(x)
511

Преобразовать эту строку в int можно функцией int(), указав базис 8
>>> int('0o755', 8)
493

Теперь посмотрим что нам возвращает os.stat
>>> perm = os.stat(path).st_mode
33261

Преобразуем в oct
>>> oct(perm)
'0o100755'

Уже почти то что надо. Чтобы оставить только нужное, отрезаем лишнее с помощью оператора & (AND). Для этого последние 3 значения ставим максимальными, остальные нулевыми.
>>> perm & 0o777
493


Оператор работает с бинарным представлением чисел, то есть операция была вот такая:
 0b100000111101101
&      0b111111111
=      0b111101101

Вспоминаем побитовые операторы Остаётся преобразовать в восьмеричное представление и убрать префикс
>>> oct(493)
'0o755'

>>> oct(493).split('o')[-1]
'755'

Именно в таком виде пермишены файла в коде обычно не используются. Хранить и использовать их удобно в виде восьмеричного int
os.chmod(path, 0o755)

или строки
os.chmod(path, int('0o755', 8))

А зачем в строке если достаточно в int? Наприрмер чтобы в json была удобочитаемая запись. Так как сериализатор запишет восьмеричное число как обычный десятичный int
>>> json.dumps(0o755)
'493'

Поэтому для удобства пишем его строкой а потом преобразуем в восьмеричный int. #tricks

Сегодня на Github закрывается доступ к git-операциям по паролю. Теперь доступ только по токену. Для входа на сайт следует настроить двухфакторку. #offtop

🐍Python и 1000 программ🐍 - канал для тех, кто программирует, или хочет научиться программировать на Python В этом канале: 📚Книги по Python 📖Уроки для начинающих и продвинутых. 💾Классные программы. 📪Свежие новости из мира Python. Переходи на канал, и учись новому! Время двигаться вперёд!📈

Часто новичкам трудно найти первую работу в айти. Море требований указанных в вакансиях кажется бездонным, а junior сейчас по комплектации навыков и знаниям - это senior 10 лет назад🔥 Знакомые ребята рассказывают простым языком о навыках и технологиях без которых невозможно найти работу в айти. Бесплатно прокачивают: - умение успешно проходить собеседования для получения сочных офферов от топовых компаний💰 - навыки и понимание технологий, необходимых для успешного получения оффера 📚 Подписывайся, учись и прокачивайся https://t.me/voiti_it

У Python есть очень удобная штука - стек вызовов. Благодаря нему модуль traceback может показать где и что сломалось в момент
У Python есть очень удобная штука - стек вызовов. Благодаря нему модуль traceback может показать где и что сломалось в момент выброса исключений. Но иногда Python просто падает без какой-либо информации. Лично меня это регулярно настигает на Windows и очень бесит. Совершенно не понятно что где произошло и как искать причину. Ставить сишные дебаггеры? Чтобы быстро понять в какой строке ошибка иногда достаточно использовать встроенный модуль faulthandler Базовое использование очень простое:
python.exe -q -X faulthandler main.py

Если интерпретатор упадёт, то вы хотя бы узнаете какая строчка привела к такому событию. Попробуйте уронить Python без faulthandler и с ним. #libs #tricks

А вы ждёте выхода Python 4? Ну зря ждёте😭 По словам Гвидо, ему хватило проблем с переходом со 2го на 3й) Лучше постепенно развивать имеющийся функционал с полной совместимостью кодовой базы чем делать такие резкие изменения. Велика вероятность что выше 3 мажорная версия более не поднимется. А как же обещания про невероятные ускорения в Python 4? Очевидно, что теперь они все будут добавляться в 3ю ветку. Вот здесь можно почитать про планы ускорения где Гвидо обещает скорость уже в 3.11 и х5 через 4 года! Здесь можно посмотреть следующие шаги по оптимизации. #offtop #2to3

Модуль objgraph позволяет нарисовать граф объектов со связями между ними. По такому визуальному представлению иногда удобно д
Модуль objgraph позволяет нарисовать граф объектов со связями между ними. По такому визуальному представлению иногда удобно дебажить архитектуру вашего кода. Особенно когда построение структуры происходит динамически. Не ставьте слишком большую глубину прорисовки, я как-то поставил 50 и ждал пол часа пока движок отрисует мне 3к нод. Вряд ли в таком графе можно что-то разобрать. К тому же рендер падает с ошибкой dot: graph is too large for cairo-renderer bitmaps. И просит уменьшить скейл до 0.2, а значит даже текста не разобрать. Так что глубина 3-5 вполне достаточно. #libs

В стандартном модуле random есть две очень похожие функции
random.randint()
random.randrange()

Обе возвращают случайное значение из указанного диапазона
>>> random.randint(10, 20)
12
>>> random.randrange(10, 20)
17

В чем же отличие? Дело в том что у randrange() есть третий параметр step. randint() действительно возвращает случайное число из указанного диапазона. randrange() на первый взгляд делает тоже самое если передать также два параметра. Но есть указать еще и step то наш диапазон усложняется, то есть в него попадёт не полный ряд значений. Например, я хочу получить случайное значение из диапазона но только чётное число. Тогда достаточно сделать так:
>>> randrange(10, 20, 2)
16

Таким образом получается что randint это частный случай randrange без указания параметра step. Еще одно важное отличие в том, что randint() включает в диапазон второе значение а randrange() нет. То есть выражение randrange(10, 20) никогда не вернёт 20, а randint(10, 20) вернёт. #tricks #basic

Наверняка у многих возникло желание потестить отправку сообщений, но Redis не установлен и вообще непонятно как с ним быть. На самом деле запустить его очень просто. 🔸 Для Windows, качаем архив, запускаем redis-server.exe 🔸 Для Linux несколько команд 🔸 Если есть Docker, то еще проще
docker run --rm  -p 6379:6379 redis

Всё, можно экспериментировать! ______________________ А еще с сервером можно поиграть в пинг-понг
> redis-cli ping
PONG

#tricks #libs #linux

Самый большой минус синхронизации из прошлого поста - нестабильность. Не знаю как у вас а у меня эта штука падала несколько раз) По моему не очень production-ready. Что же делать? Отправлять disckcache на сетевой диск? Не, я бы не стал. Ведь есть отличная альтернатива! Это Redis. Redis это жутко быстрая in-memory база данных. Запись в неё похожа на документоориентированные NoSQL базы данных. То есть без схемы, без таблиц. Просто ключ=значение. Redis используется для кэширования и как брокер для передачи сообщений. Имеется подписка на изменения и время жизни записей. Вот пример кода: import redis R = redis.Redis() R.set('key', 'value') R.get('key') # b'value' Имея такой функционал, давайте реализуем что-то очень удобное для обмена данными по сети... Хотя подождите ка, всё уже придумано до нас! И это проект PyRSMQ Что он делает? Создаёт очередь сообщений которые может забирать другой клиент. Как это организовать? Для начала поднимаем cервер Redis. Потом на одном хосте создаём очередь для отправки.
queue = RedisSMQ(
    host=host,
    port=port,
    qname='example'
)
queue.exceptions(False).\
  createQueue(delay=0).\
  vt(message_live_time).\
  execute()

На другом хосте содаём клиента для прослушивания очереди
on_receive = lambda msg: print(msg)

consumer = RedisSMQConsumer(
  'example', on_receive
  host=host, port=port)

Начинаем прослушивание очереди
consumer.run()

Теперь можем отправлять сообщения
queue.sendMessage(delay=0).message(msg).execute()

Теперь все сообщения, отправленные в очередь, будут попадать в наш колбек
on_receive
. ▫️Обязательное условие — наличие поднятого Redis-сервера. ▫️Единственное ограничение, обусловленное спецификой инструментов, данные должны быть JSON serializable. #libs

От многопоточных вычислений переходим к распределённым. То есть вычисления, происходящие на нескольких компьютерах. Конечно, в зависимости от задачи, вы можете взять готовые решения вроде CGRU или Deadline для рендеринга, charm4py или Dask для ML, или замутить что-то на AWS С2. Но хотелось бы чего-то попроще, попитоничней что ли) А ведь в Python есть средства "из коробки" для синхронизации нескольких процессов на разных хостах. Вот простой пример кода, который синхронизирует работу двух процессов на разных компьютерах. В этом случае используется процесс-посредник, который является синхронизирующим сервером. В примере создаётся некий Manager, который шарит общую для клиентов очередь. Все подключившиеся могут что-то в неё писать или забирать. В моём коде один процесс что-то "считает" и складывает в очередь, другой забирает и продолжает какие-то свои "расчёты". Если у вас есть несколько машин, то можете попробовать это запустить по сети (нужно заменить 'localhost' на IP-адрес сервера). Но и на локальной машине сработает. Gist 🌎 #libs #source #tricks