All streams
Search
Write a publication
Pull to refresh
1
0

Пользователь

Send message

TL;DR - если лень, то можно использовать unknown, но не any. any - зло!

Функция глубокого копирования по аналогии с lodash cloneDeep

Клонирование/копирование идеальный случай для обобщённого типа (собственно lodash cloneDeep так и типизирован с использованием generic типа)

блоки catch
Это вынужденное обстоятельство в связи с плохим дизайном ошибок в JS/TS

Т.е. речь о ситуациях, когда нам придётся либо обвешиваться type guard, что не гарантирует что все случаи предусмотрели (человеческий фактор / неопытность разработчика), либо как раз создадим головную боль потребителю, которому ts будет ругаться, что его тип не совместим с unknown.

Для новичка простительно использование any, для опытного разработчика - нет. Лучше всего использовать обобщённые типы. Если не очень хочется возится с ними, то используем unknown, который проверяется на месте или же передаётся куда-то в глубь дальше.

Например в случае с глубоким копированием, лично я, не вижу смысла делать эту функцию типобезопасной, потому что туда передать могут реально практически что угодно - в этом смысл, а следовательно и проще / дешевле для разработки использование any будет более чем оправданно

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

Действительно типобезопасный debounce можно состряпать как-то так:

declare function debounce<P extends unknown[]>(fn: (...args: P) => void, timeoutMS: number): (...args: P) => void;

const debouncedAlert = debounce(alert, 1000);

В этом примере используется unknown, но я вроде как тут он не нарушает безопасность типов.

А можно, пожалуйста, пример этого самого "мата в 3 этажа" при использовании unknown? Просто мне сложно представить ситуацию, в которой вместо конкретных или обобщённых типов, приходится городить непонятное нечто через any/unknown.

Думаю это хороший подарок для тех кто обучают языку Java других людей. Объяснять новичку что это за class Main public static void main немного затруднительно, ведь в процессе обучения до классов и методов доходят далеко не сразу.

Почему не выполнилась эта функция? Ой, я забыл сделать для неё await.

У некоторых разработчиков кривые руки, раз они забывают сделать await.

Ошибка в корутине? А ты точно запустил её с нужными параметрами?

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

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

Асинхронная работа с диском непосредственно в ОС реализована плохо, что каким-то образом является недостатком asyncio по сравнению с синхроным/многопоточным подходом.

В Textual я наблюдаю следующую часто возникающую проблему: разработчики тестируют конкурентность, добавляя вызов a time.sleep(10), чтобы симулировать планируемую ими работу.

У некоторых разработчиков кривые руки, ведь они в асинхронных функциях пишут синхронный time.sleep().

C#, язык, из которого позаимствован синтаксис async/await, имеет более широкую поддержку async в базовых библиотеках ввода-вывода, потому что он реализует Task‑based Asynchronous Pattern (TAP), где задачи диспетчеризируются в управляемом пуле потоков.

Здесь всё верно. Асинхронность в питоне плохо вплетена в язык. Многие модули со временем получают асинхронные альтернативы (например, subprocesses), но этого мало.

Я мейнтейнер пакетов, поэтому могу сказать, что поддержка и синхронных, и асинхронных API — сложная задача.

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

Лично я уже слабо себе представляю разработку на питоне без асинхронности, но как уже написали выше:

Асинхронщину не все понимают. Плюс, она не всем нужна.

Zig это нечто промежуточное между C и Rust. Zig имеет множество крутых фич как в Rust (tagged unions, optionals, явные ошибки), но при этом имеет более свободную модель выделения памяти как в C (аллокаторы и defer очень удобны, но всё же полностью не защищают от утечек/двойного освобождения).

Если неявный nullability это ошибка на миллиард долларов, то контекст функций в JS это ошибка где-то на 100000 баксов. Это же нисколько не нормально, что сохранив ссылку на, казалось бы, метод объекта, на самом деле мы получаем ссылку на unbound функцию. Вот пример для иллюстрации:

const arr = [1, 2, 3];
arr.map(v => v * v); // ок

const arrMap = arr.map;
arrMap(v => v * v); // не ок: TypeError: can't convert undefined to object

Благо завезли стрелочные функции которые хоть как-то спасают ситуацию.

А зачем так извращаться? PNG поддерживает текстовые чанки и туда можно запихать любые данные

Если функция не собирается менять коллекцию которую ей передают на вход, то имеет смысл использовать соответствующую аннотацию типа(Sequence вместо list/tuple, Mapping вместо dict, AbstractSet вместо set)

def process_sequence(items: Sequence[...] = ()): # сюда можно передать как list/tuple, так и любую кастомную реализацию Sequence
    ...

def process_mapping(items: Mapping[..., ...] = {}): # так как мы не планируем менять входные данные, то изменяемый dict как значение по умолчанию не навредит
    ...

Стоит ещё учитывать, что байткод питона не имеет какой-то спецификации и не обязан иметь обратную совместимость между версиями. Например, в версии питона 3.12 для спискового включения не создаётся отдельный кодовый объект, а включение инлайнится напрямую и в таком случае включение должно быть ещё на долю процента быстрее цикла

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

Про лямбды и varargs(понятия не имею как перевести на русский) даже сказать нечего. С тем же успехом вы могли показать конкатенацию строк и назвать это полезной фичей и, формально, вы даже были бы правы.

Честно, в статье я ожидал увидеть какие-то приколюхи с рантаймом, по большей части, так как синтаксис у жабы довольно минималистичный(за это его и любим). Если уж рассказывать про синтаксические фичи какого-то языка, то в этом плане какой-нибудь тайпскрипт был бы гораздо интереснее.

А так и нельзя писать

>>> a := 123
  File "<stdin>", line 1
    a := 123
      ^^
SyntaxError: invalid syntax

У scalars не нужно писать all() - оно по умолчанию установлено

Это не так. result.scalars() привязан к соединению из пула и к сессии, result.scalars().all() нет. В теории, это может вызвать проблемы при формировании ответа на запрос, ведь сессия в данный момент уже будет закрыта.

Метод session.stream_scalars(query) обеспечивают возврат асинхронной версии объекта, который поддерживает протокол асинхронной итерации Python.

stream_scalars по большей части нужен для работы с большим объёмом данных. Если данных немного, то лучше сразу забрать их все через scalars().all().

То есть для его итерации вы будете использовать не простой цикл for, а асинхронный async for . Он позволяет выполнять асинхронные операции в процессе итерации.

async for служит для других целей. С синхронным итератором вы тоже можете выполнять асинхронные операции в процессе итерации.

Так и должна работать любая мультиплеерная игра с элементом соревновательности. Клиенту доверять нельзя, это же база

Руки бы поотрывать тому, кто меняет сигнатуру публичной функции без сохранения обратной совместимости. И это безотносительно ЯП.

Нет. Класс не наследует свойства класса type, а является производной работы type. В качестве доказательства: по умолчанию у класса нет методов что есть у type, хотя их можно добавить.

Еще раз. Класс не является наследником type, он является результатом работы функции конструктора класса.

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

Поправляю, Path наследован class PurePath(object), который, в свою очередь, ведет себя как metaclass, переопределяя new.

Переопределенный div позволяет использовать синтаксис Path('root') / Path('folder'). В этом примере на команду div происходит Path('root').parts + Path('folder').parts. Но Path в реальности это просто маска двух классов:cls = PureWindowsPath if os.name == 'nt' else PurePosixPath. Потому переопределив что-то у класса Path ничего не получим, надо переопределять у PureWindowsPath и PurePosixPath.

Формально, Path можно назвать метаклассом. Но чаще всего под метаклассами всё же имеют ввиду наследников класса type.
Но да, если рассматривать Path в таком ключе, то вы правы.

Классы задают базовое поведение и задают шаблоны атрибутов для объектов класса. Любой класс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить метакласс класса можно командой type(cls). Именно потому, что класс это тоже объект.

Откуда это взялось? Или я_точно_синглетон = dict() тоже синглетон? Класс это экземпляр класса type или одного из его наследников, не более.

Метакласс имеет не так много методов, поскольку не так уж и много нам надо делать на этом уровне. Базовый метакласс, это который type, например, при создании любого класса добавляет ему набор стандартных дандер-методов, типа getattr, и т.п. В твоем примере ты создал class MyClass() ничего ему не объявил, а у класса есть метод str. Спасибо метаклассу type который по умолчанию является конструктором класса и делает всю грязную работу за нас.

type ничего не добавляет своим наследникам. Единственное что type.new делает, так это создаёт новый экземпляр переданного класса и заполняет его словарь, используя переданные параметры. Ну и валидацию выполняет, чтобы от некоторых типов не наследовались, но это не суть важно.

Вот второй пример применения метакласса: вместо ручного прописывания методов у каждого дочернего класса, делаем это через метакласс. Кстати, я предпочитаю использовать для этого миксины. В dataclass - это же делают через декоратор.

Это плохой пример использования метаклассов. Как вы уже и написали, миксины для этого подойдут гораздо лучше. Конечно, не стоит специально избегать создание метаклассов, но и городить их почём зря, это тоже не лучшая практика.
Действительно хорошим примером метакласса может послужить Enum из стандартной библиотеки.

Поскольку Метакласс позволяет переопределять дандер и не только методы на момент объявления класса (это же возможно сделать и миксином и наследованием и декоратором) то можно переопределить "div" и всю остальную математику и получить класс Path из Pathlib который реализует DSL, в данном случае это привычный file system syntax. Но важно, что этот класс на самом деле - это несколько классов PosixPath, WindowsPath etc. Без метакласса надо было бы переопределять математику в каждом из них ручками. Количество кода увеличилось бы. А через метакласс удалось соблюсти DRY да и KISS тоже где-то рядом.

Насколько я знаю, pathlib никаким образом не использует метаклассы. Поправьте, если я ошибаюсь. И даже если использует, то зачем в теории может понадобиться переопределять div на каком-нибудь PathType, а не на самом Path?

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

Information

Rating
4,369-th
Registered
Activity