Aspiring Data Science
366 subscribers
413 photos
11 videos
10 files
1.82K links
Заметки экономиста о программировании, прогнозировании и принятии решений, научном методе познания.
Контакт: @fingoldo

I call myself a data scientist because I know just enough math, economics & programming to be dangerous.
Download Telegram
#python #codegems

Ключевое слово for работает с итерируемыми объектами. А ключевое слово await – с объектами, допускающими ожидание.

В настоящее время asyncio не предоставляет асинхронного API файловой системы – в отличие от Node.js.

Начиная с Python 3.9 сопрограмма asyncio.to_thread упрощает делегирование файлового ввода-вывода пулу потоков, предоставляемому библиотекой asyncio.

Под капотом цикл событий asyncio обращается к .send, чтобы привести в действие ваши сопрограммы, а ваши сопрограммы с помощью await вызывают другие сопрограммы, в т. ч. библиотечные. await заимствует большую часть реализации у предложения yield from, которое также обращается к .send для управления сопрограммами.

Цепочка await в конце концов достигает низкоуровневого объекта, допускающего ожидание, который возвращает генератор, к которому цикл событий может обращаться в ответ на такие события, как срабатывание таймера или сетевой ввод-вывод. Низкоуровневые объекты, допускающие ожидание, и генераторы в конце таких цепочек await находятся глубоко внутри библиотек, они не являются частью их API и могут быть расширениями, написанными на C.

Для достижения максимальной производительности при работе с asyncio мы должны заменить все функции, осуществляющие ввод-вывод, асинхронными версиями, которые активируются в результате выполнения await или asyncio.create_task, для того чтобы управление возвращалось циклу событий, пока функция ждет завершения ввода-вывода.

У asyncpg есть важное достоинство – он позволяет обойти отсутствие в PostgreSQL поддержки высокой конкурентности (в этой СУБД используется один серверный процесс на каждое подключение), поскольку реализует пул подключений для внутреннего подключения к самой Postgres.

Эдсгер Дейкстра изобрел семафор в начале 1960-х годов. Идея простая, но настолько гибкая, что большинство других объектов синхронизации, например блокировки и барьеры, можно построить на основе семафоров. В стандартной библиотеке Python есть три класса Semaphore: по одному в модулях threading,multiprocessing и asyncio.

В классе asyncio.Semaphore имеется внутренний счетчик, который уменьшается на 1 всякий раз, как выполняется await для метода-сопрограммы .acquire(), и увеличивается на 1 при вызове метода .release(), который не является сопрограммой, потому что никогда не блокирует выполнение. Начальное значение счетчика задается при создании объекта Semaphore:
semaphore = asyncio.Semaphore(concur_req)

Ожидание .acquire() не приводит к задержке, когда счетчик больше 0, но если счетчик равен 0, то .acquire() приостанавливает ожидающую сопрограмму до тех пор, пока какая-нибудь другая сопрограмма не вызовет .release() для того же семафора, увеличив тем самым счетчик. Вместо того чтобы обращаться к этим методам напрямую, безопаснее использовать semaphore как асинхронный контекстный менеджер:
async with semaphore:
image = await get_flag(client, base_url, cc)


Стоит написать первое async def, как в программе неизбежно будут появляться все новые и новые async def, await,
async with и async for. И внезапно использование неасинхронных библиотек становится проблемой.

Атрибуты-данные и методы в Python носят общее название «атрибуты»; метод – это просто вызываемый атрибут. Помимо атрибутов-данных и методов,мы можем создавать еще свойства, позволяющие заменить открытые атрибуты-данные методами-акцессорами (т. е. методами чтения и установки), не изменяя интерфейс класса.

Пользовательский класс, в котором имеется метод getattr, может реализовать вариант динамических атрибутов, который я называю виртуальными атрибутами; они не объявлены в исходном коде класса и отсутствуют в экземпляре dict, но могут быть получены из какого-то другого места или вычислены «на лету», когда программа пытается прочитать несуществующий атрибут, например obj.no_such_attr.

Функция keyword.iskeyword(…) позволяет проверить, является ли строка ключевым словом языка.
#python #codegems

Встроенная функция property часто используется как декоратор, но в действительности она является классом. В Python функции и классы нередко взаимозаменяемы, поскольку являются вызываемыми объектами и не существует оператора new для создания объекта, поэтому вызов конструктора ничем не отличается от вызова фабричной функции. Вот полная сигнатура конструктора класса property: property(fget=None, fset=None, fdel=None, doc=None).

Функция vars не умеет работать с классами, в которых определен атрибут slots и нет атрибута dict (в отличие от функции dir, которая справляется с такими экземплярами).
Без аргумента vars() делает то же самое, что locals(): возвращает словарь, описывающий локальную область видимости.

Метод getattr всегда вызывается после getattribute и только в том случае, когда getattribute возбуждает исключение AttributeError. Чтобы при
получении атрибутов obj не возникало бесконечной рекурсии, в реализации getattribute следует использовать super().getattribute(obj, name).

Встроенный тип type на самом деле является метаклассом – классом по умолчанию для определенных пользователем классов.

В способе обработки атрибутов в Python существует важная асимметрия. При чтении атрибута через экземпляр обычно возвращается атрибут, определенный в этом экземпляре, а если такого атрибута в экземпляре не существует, то атрибут класса. С другой стороны, в случае присваивания атрибуту экземпляра обычно создается атрибут в этом экземпляре, а класс вообще никак не затрагивается. Эта асимметрия распространяется и на дескрипторы, в результате чего образуются две категории дескрипторов, различающиеся наличием или отсутствием метода set. Если set присутствует, то класс является переопределяющим дескриптором, иначе непереопределяющим.

Требование явно объявлять self первым аргументом методов – одно из противоречивых проектных решений в Python.Простота – даже элегантность – реализации достигается за счет пользовательского интерфейса: сигнатура метода – def zfill(self, width) – визуально не соответствует его вызову – label.zfill(8).

Метапрограммирование классов – это искусство создания или настройки классов во время выполнения. Классы в Python – полноправные объекты, поэтому функция может в любой момент создать новый класс, не используя ключевое слово class. Декораторы классов – также функции, которые дополнительно умеют инспектировать и изменять декорированный класс и даже заменять его другим. Наконец, метаклассы – самое продвинутое средство метапрограммирования классов: они позволяют создавать целые категории классов со специальными характеристиками.

Обычно мы воспринимаем type как функцию, которая возвращает класс объекта, потому что именно это делает выражение type(my_object): возвращает my_object.class.
Однако type – это класс, который создает новый класс, если вызывается с тремя аргументами. Рассмотрим следующий простой класс:
class MyClass(MySuperClass, MyMixin):
x = 42
def x2(self):
return self.x * 2

С помощью конструктора type мы можем создать MyClass во время выполнения:
MyClass = type('MyClass',
(MySuperClass, MyMixin),
{'x': 42, 'x2': lambda self: self.x * 2},
)

Этот вызов type функционально эквивалентен предыдущему предложению
блока class MyClass.
#python #codegems

Дескрипторы – это способ повторного использования одной и той же логики доступа в нескольких атрибутах. Например, типы полей в объектно-ориентированных отображениях вроде Django ORM и SQL Alchemy – дескрипторы, управляющие потоком данных от полей в записи базы данных к атрибутам Python-объекта и обратно. Дескриптор – это класс, который реализует динамический протокол, содержащий методы get, set и delete. Класс property реализует весь протокол дескриптора.

Пример использования дескриптора:
class Quantity:
def __set_name__(self, owner, name):
self.storage_name = name
def __set__(self, instance, value):
if value > 0:
instance.__dict__[self.storage_name] = value
else:
msg = f'{self.storage_name} must be > 0'
raise ValueError(msg)
# __get__ не нужен
class LineItem:
weight = Quantity()
price = Quantity()
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price


Глядя на пример, можно подумать, что кода слишком много для управления всего-то парой атрибутов, но важно понимать, что логика дескриптора теперь вынесена в отдельную кодовую единицу: класс Quantity. Обычно мы не определяем дескриптор в том же модуле, в каком он используется, а заводим отдельный служебный модуль, предназначенный для использования во всем приложении, а то и во многих приложениях, если разрабатывается библиотека или фреймфорк.

Воображаемый магазин натуральных пищевых продуктов столкнулся с неожиданной проблемой: каким-то образом была создана строка заказа с пустым описанием, и теперь заказ невозможно выполнить. Чтобы предотвратить такие инциденты в будущем, мы создадим новый дескриптор, NonBlank. Проектируя NonBlank, мы обнаруживаем, что он очень похож на дескриптор Quantity, а отличается только логика проверки. Это наводит на мысль о рефакторинге и заведении двух базовых классов: завести абстрактный класс Validated, переопределяющий метод set, вызывая метод validate, который должен быть реализован в подклассах. Затем мы переписываем Quantity и реализуем NonBlank, наследуя классу Validated, так что остается лишь написать методы validate. Соотношение между классами Validated, Quantity и NonBlank – пример паттерна проектирования Шаблонный метод, который в классической книге «Паттерны проектирования» описывается следующим образом: Шаблонный метод определяет алгоритм в терминах абстрактных операций, которые переопределяются в подклассах для обеспечения конкретного поведения.

import abc
class Validated(abc.ABC):
def __set_name__(self, owner, name):
self.storage_name = name
def __set__(self, instance, value):
value = self.validate(self.storage_name, value)
instance.__dict__[self.storage_name] = value
@abc.abstractmethod
def validate(self, name, value):
"""вернуть проверенное значение или возбудить ValueError"""

class Quantity(Validated):
"""число, большее нуля"""
def validate(self, name, value):
if value <= 0:
raise ValueError(f'{name} must be > 0')
return value
class NonBlank(Validated):
"""строка, содержащая хотя бы один символ, отличный от пробела"""
def validate(self, name, value):
value = value.strip()
if not value:
raise ValueError(f'{name} cannot be blank')
return value

import model_v5 as model
class LineItem:
description = model.NonBlank()
weight = model.Quantity()
price = model.Quantity()
def __init__(self, description, weight, price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
#python #fun

Goose Typing
#python #books

Ссылки на посты по книжке "Л. Рамальо. Python – к вершинам мастерства: Лаконичное и эффективное программирование" (в оригинале - Fluent Python, 2nd Edition). Содержат материал, который показался мне интересным и вошёл в категорию #codegems.

Затрагиваются механизмы сопоставления (match), классы данных и их аналоги, аннотирование типами, инструменты itertools, работа с классами/ООП, генераторы, контекстные менеджеры, асинхронка, дескрипторы классов.

Пробегитесь по темам, если есть незнакомые слова, возможно, есть смысл перечитать актуальную доку Питон )

1. [], {} и ()/match/ChainMap/MappingProxyType
2. class init/dict/json
3. unicode: NFC/NDF, strxfrm/NamedTuple/dataclass
4. more dataclass/typehints
5. weakrefs/functional programming/more typehints
6. Any/|/TypeVar/TypeAlias/typing.Protocol
7. positional-only/closures/singledispath/decorator via class
8. getattr/reduce via initializer/zip,zip_longest/principle of failing fast
9. goose typing/vurtual subclass/Hashable/ABC/Decimal
10. UserDict, UserList, UserString/MRO/mixin/get_annotations
11. (sub)generator/coprogram/type: ignore/with/@contextmanager
12. else in for,while,try/scientific sins/GIL/getswitchinterval/asyncio
13. asyncio.to_thread/asyncpg/asyncio.Semaphore/async with/keyword.iskeyword
14. property/vars/metaprogramming
15. class descriptors

Если решите читать книгу - ТОЛЬКО в оригинале, русский перевод плох.
#python #typing

Про Final не знал. Self, TypeAlias, Literal заслуживают внимания.
На самом деле всё ещё сложнее с TypeVar.

https://medium.com/techtofreedom/8-levels-of-using-type-hints-in-python-a6717e28f8fd
#python #security #pypi

"Проблема возникла из-за удаления зависимости msgspec-python313-pre из репозитория PyPI (Python Package Index), который содержит тысячи сторонних модулей для Python. Злоумышленники добавили в репозиторий вредоносный пакет с тем же именем, который даёт им возможность удалённого выполнения произвольного кода на уязвимых системах, что может привести к компрометации данных, краже информации и полному контролю над заражённым ПК."

https://3dnews.ru/1119504/millioni-kompyuterov-okazalis-pod-ugrozoy-vzloma-izza-kriticheskoy-uyazvimosti-svyazannoy-s-python
#python #codegems

Как узнать, кто тянет вас на дно?! )

python -X importtime -c "from mymodule import *" 2>import_times.txt