Python Заметки

Description
Интересные заметки и обучающие материалы по Python

Контакт: @paulwinex

⚠️ Рекламу на канале не делаю!⚠️

Хештеги для поиска:
#tricks
#libs
#pep
#basic
#regex
#qt
#django
#2to3
#source
#offtop
Advertising
We recommend to visit

Бизнес блог #1
Выжимаю книги до самой сути.

? Реклама - @jaMasha

? Хотите свою книгу? Мы напишем её за вас и сделаем книгу бестселлером. Подробности в боте @Summary_library_bot

? Оставьте след в истории с помощью книги
https://expert-book.pro

Фильмы и сериалы со всей планеты. Мы знаем, что посмотреть, где посмотреть и на что сходить в кино.

Last updated 2 months, 4 weeks ago

Все материалы размещены по партнёрской програме ivi.ru | All materials are posted on the partner program ivi.ru

По всем вопросам: @kuzr103
Купить рекламу: https://telega.in/c/k1noxa103
Основной канал: https://t.me/kino_hd2

Last updated 5 days, 22 hours ago

4 months ago

Объекты datetime.timedelta поддерживают операторы деления и умножения

```
from datetime import timedelta

td1 = timedelta(hours=1)
# увеличим интервал в 2.5 раза
print(td1*2.5)
# 2:30:00

# разделим интервал на 2
print(td1/2)
# 0:30:00
```

Можно разделить один интервал на другой, включая целочисленное деление. Так мы узнаем сколько раз один период помещается в другой.

td2 = timedelta(minutes=25) print(td1/td2) \# 2.4 print(td1//td2) \# 2

А так же остаток от делния.

print(td1%td2) \# 0:10:00

Объекты datetime.timedelta поддерживают отрицательные значения. Эти две записи идентичны.

datetime.now() \- timedelta(hours=1) datetime.now() + timedelta(hours=\-1)

И, что очевидно, операторы сравнения

td1>td2 \# True

А еще можно почитать про форматирование даты и времени здесь и здесь.

#tricks

Telegram

Python Заметки

Все знают как красиво написать дату и время с помощью библиотеки datetime: >>> from datetime import datetime >>> >>> dt = datetime.now() >>> dt.strftime('%Y.%m.%d %H:%I') '2020.01.08 12:00' Но мало кто знает, что тоже самое можно сделать и другим способом:…

4 months ago

Три способа создать декоратор для метода класса.

*▫️Способ 1*. Обычная функция.

Единственное отличие от простого декоратора функции в том, что нужно учитывать аргумент self.
Если же он не нужен то просто пробрасываем его через *args

```
def decorator_func(func):
def wrapped(args, kwargs):
print('decorator_func')
return func(
args, **kwargs)
return wrapped

class MyClass:
@decorator_func
def method(self):
print('call method')

MyClass().method()
# decorator_func
# call method
```

▫️Способ 2. Методы класса.

Но что, если декоратор жестко привязан к классу и используется только в нём. И стоит задача закрепить декоратор именно за этим классом и расположить внутри него.
В таком случае можно сделать staticmethod. Это будет выглядеть страшно, но работать будет (тестировано на 3.11)
Очевидно, что декоратор должен быть объявлен раньше метода.

```
class MyClass:
@staticmethod
def decorator(func):
def wrapper(args, kwargs):
print('decorator from staticmethod')
return func(
args, **kwargs)
return wrapper

@decorator.\_\_func\_\_ def method(self): print('method called')

MyClass().method()
# decorator from staticmethod
# method called
```

Тоже самое будет и с classmethod, но еще хуже.

```
class MyClass:
@classmethod
def decorator(func):
def wrapper(self, args, kwargs):
print('decorator from classmethod')
return func(self,
args, **kwargs)
return wrapper

@decorator.\_\_func\_\_ def method(self): print('method called')

MyClass().method()
# decorator from classmethod
# method called
```

Где-то потерялся аргумент cls. Скорее всего это можно решить но лучше не надо. Оба варианта выглядят страшненько ?

▫️Способ 3. Вложенный класс и staticmethod

```
class MyClass:
class deco:
@staticmethod
def my_decorator(func):
def wrapper(args, kwargs):
print('decorator from subclass')
return func(
args, **kwargs)
return wrapper

@deco.my\_decorator def method(self): print('method called')

MyClass().method()
# decorator from subclass
# method called
```

Получаем чтото вроде микса способов 1 и 2: функция вложена в отдельный класс.

Лучшей практикой является способ 1 - обычные функции.

Всего пару раз за практику я использовал 3й способ, когда декоратор был намертво привязан к классу и нигде больше не мог использоваться (например, отправлял вызов метода на воркера в другой процесс, не спрашивайте почему так, просто так было нужно ?)

Способ 2 не советую. Это, скорей, разминка для ума чем практический пример.

PS
- wraps пропустил для краткости
- в коментах дополнительная инфа

#tricks

4 months, 3 weeks ago

Нередко требуется удалять дубликаты инстансов класса. Для этого обычно используется либо циклы со сравнением некоторых атрибутов, либо тип данных set().

При добавлении элемента в set происходит сравнение этого объекта по хешу. Если хеш совпадает с хешем уже существующего объекта, то происходит сравнение объектов на равенство. Если объекты равны, то новый объект не добавляется.

```
class A:
def __init__(self, pk: int):
self.pk = pk
def __repr__(self):
return f"{self.__class__.__name__}(pk={self.pk})"

set([A(pk=1), A(pk=2), A(pk=2)])

{A(pk=1), A(pk=2), A(pk=2)}
```

Далее для краткости метод \_\_repr\_\_() я буду пропускать

По умолчанию в расчёте хеша, помимо прочего, используется адрес в памяти, который можно получить с помощью функции id(), поэтому все объекты считаются разными. Чтобы изменить способ сравнения объектов нам требуется переопределить метод __eq__()

```
class A:
def __init__(self, pk: int):
self.pk = pk
def __eq__(self, other):
return self.pk == other.pk

set([A(pk=1), A(pk=2), A(pk=2)])

TypeError: unhashable type: 'A'
```

Теперь в дело вступает логика, описаная в документации.
Если вы переопределили __eq__() то следует переопределить и __hash__().

```
class A:
def __init__(self, pk: int):
self.pk = pk
def __eq__(self, other):
return self.pk == other.pk
def __hash__(self):
return hash(self.pk)

set([A(pk=1), A(pk=2), A(pk=2)])

{A(pk=1), A(pk=2)}
```

Отлично, теперь всё работает.
Этот же принцип действует и при наследовании. Допустим, вы создали дочерний класс

```
class B(A):
pass

set([B(pk=1), B(pk=2), B(pk=2)])

{B(pk=1), B(pk=2)}
```

Теперь следует учитывать вот такое поведение

```
hash(A(1)) == hash(B(1))

True
set([A(1), B(1)])
{A(pk=1)}
```

Инстансы А и В могут считаться идентичными, если они имеют одинаковые значения атрибутов и хеш, что может привести к неожиданным результатам при использовании множеств. Нужно учесть это в методах:

```
class A:
...
def __eq__(self, other):
return isinstance(other, self.__class__) and self.pk == other.pk

def \_\_hash\_\_(self): return hash((self.pk, self.\_\_class\_\_)) ...

```

Но если вдруг решите как-то изменить способ сравнения в классе В...

```
class B(A):
def __eq__(self, other):
return abs(self.pk) == abs(other.pk)

set([B(pk=1), B(pk=2), B(pk=2)])

TypeError: unhashable type: 'B'
```

Снова получите ошибку. Та же логика - при переопределении метода __eq__() в новом классе метод __hash__() автоматически становится None и его тоже требуется переопределить.

#tricks

Python documentation

3. Data model

Objects, values and types: Objects are Python’s abstraction for data. All data in a Python program is represented by objects or by relations between objects. (In a sense, and in conformance to Von ...

Нередко требуется удалять дубликаты инстансов класса. Для этого обычно используется либо циклы со сравнением некоторых атрибутов, либо тип данных `set()`.
6 months, 2 weeks ago
7 months, 3 weeks ago

Библиотека APScheduler для управления заданиями в Python.
Может запускать планировщик и задания как отдельный поток (синхронный код) и как коркутины (асинхронный код), отложенные или через интервал.

Что есть в APScheduler:

▫️гибкий функционал создания задачи
▫️удобное управление созданными заданиями (pause\resume, listing, modify, reschedule)
▫️кастомизация классов библиотеки
▫️различные хранилища заданий (Memory и различные БД)
▫️интеграции в фреймворки
▫️7 вариантов планировщика

Три варианта тригеров для задач:

▫️по дате с помощью datetime
▫️через интервал с помощью datetime
▫️через интервал с помощью cron

и другие полезности

В данный момент готовится к релизу 4я версия

PS. Всегда использую вместе с FastAPI, очень рекомендую к ознакомлению.

#libs

GitHub

GitHub - agronholm/apscheduler at 3.x

Task scheduling library for Python. Contribute to agronholm/apscheduler development by creating an account on GitHub.

Библиотека [APScheduler](https://github.com/agronholm/apscheduler/tree/3.x) для управления заданиями в Python.
8 months, 1 week ago
***⭐️*** Поздравляю!!! ***⭐️***

⭐️ Поздравляю!!! ⭐️
????‍♀️???

+ бонус в коментах ?**

8 months, 2 weeks ago

Когда пишешь асинхронный код нужно учитывать особенности такого подхода. Всегда требуется держать в уме, когда возвращается корутина а когда реальный результат. Между этими двумя сущностями должен быть вызов через await.
Вот пример синхронного запроса в базу данных с помощь sqlalchemy. Query пишу инлайном для компактности.

entities = session.execute(select(EntityModel)).scalars().all()

Всё ясно и линейно. А вот он же асинхронный.

result = await session.execute(select(EntityModel)) entities = result.scalars().all()

Это значит что session.execute возвращает корутину, или awaitable объект. Сначала его нужно выполнить через await, тогда получишь объект с которым можно дальше работать.
Не хочу сказать что это мастхэв практика, но простые асинхронные запросы тоже можно сократить до одной строки. Просто использовать скобки.

entities = ( await session.execute(select(EntityModel)) ).scalars().all()

На самом деле я использую такую конструкцию только в прототипах тестов или вспомогательных функциях тестов. В продакшн такое обычно не попадает.

#tricks

9 months, 1 week ago

PEP471 добавил в Python3.5 в модуль os новую функцию scandir()

▫️это генератор с соответствующими возможностями
▫️возвращает не просто строку а объект DirEntry
▫️работает в 4-10 раз быстрей чем os.listdir и os.walk

Раньше это была отдельная библиотека, которая позже стала частью CPython, как и ряд других новых библиотек в Python 3.

В настоящий момент метод Path.iterdir() всё еще использует os.listdir().
Обёртка, заставляющая обычную функцию работать как генератор

def iterdir(self): for name in os.listdir(self): yield self.\_make\_child\_relpath(name)

В тоже время Path.glob() и Path.rglob() уже используют os.scandir(), то есть полноценные генераторы.

#libs

peps.python.org

PEP 471 – os.scandir() function – a better and faster directory iterator | peps.python.org

Python Enhancement Proposals (PEPs)

10 months, 2 weeks ago
Поздравляю всех с 2к24! ***❄️******⛄️******?******?******?***

Поздравляю всех с 2к24! ❄️⛄️???

10 months, 3 weeks ago

Библиотеки для рабты с коллекциями файлов (секвенциями)

▫️ Поиск коллекций в директории
▫️ Проверка целостности
▫️ Поиск пересечений
▫️ Форматирование
И другие функции

*➡️ CLIQUE* https://clique.readthedocs.io/en/stable/

import clique files = [ '/tmp/file1\_001.png', '/tmp/file1\_002.png', '/tmp/file1\_003.png', '/tmp/file1\_005.png', ] collection = clique.assemble(files)[0][0] collection.head \# '/tmp/file1\_' collection.tail \# '.png' collection.padding \# 3 collection.indexes \# <SortedSet "[1, 2, 3, 5]"> collection.holes() \# <Collection "/tmp/file1\_%03d.png [4]"> collection.separate() \# [<Collection "/tmp/file1\_%03d.png [1\-3]">, \# <Collection "/tmp/file1\_%03d.png [5]">]

➡️ PYSEQ https://pyseq.rsgalloway.com/

import pyseq files = [ '/tmp/file1\_001.png', '/tmp/file1\_002.png', '/tmp/file1\_003.png', '/tmp/file1\_005.png', ] sequence = pyseq.Sequence(files) sequence.head() \# 'file1\_' sequence.tail() \# '.png' sequence.path() \# '/tmp/file1\_1\-5.png' sequence.frames() \# [1, 2, 3, 5] sequence.format('%p') \# '%03d' sequence.missing() \# [4]

У библиотек схожий функционал но в деталях различается.
clique не умеет работать с pathlib.Path а pyseq не понимает генератор как источник. Но обе могут найти все коллекции в директории и выдать много информации о них.

#libs

We recommend to visit

Бизнес блог #1
Выжимаю книги до самой сути.

? Реклама - @jaMasha

? Хотите свою книгу? Мы напишем её за вас и сделаем книгу бестселлером. Подробности в боте @Summary_library_bot

? Оставьте след в истории с помощью книги
https://expert-book.pro

Фильмы и сериалы со всей планеты. Мы знаем, что посмотреть, где посмотреть и на что сходить в кино.

Last updated 2 months, 4 weeks ago

Все материалы размещены по партнёрской програме ivi.ru | All materials are posted on the partner program ivi.ru

По всем вопросам: @kuzr103
Купить рекламу: https://telega.in/c/k1noxa103
Основной канал: https://t.me/kino_hd2

Last updated 5 days, 22 hours ago