TL;DR - если лень, то можно использовать unknown, но не any. any - зло!
Функция глубокого копирования по аналогии с lodash cloneDeep
Клонирование/копирование идеальный случай для обобщённого типа (собственно lodash cloneDeep так и типизирован с использованием generic типа)
блоки catch Это вынужденное обстоятельство в связи с плохим дизайном ошибок в JS/TS
Т.е. речь о ситуациях, когда нам придётся либо обвешиваться type guard, что не гарантирует что все случаи предусмотрели (человеческий фактор / неопытность разработчика), либо как раз создадим головную боль потребителю, которому ts будет ругаться, что его тип не совместим с unknown.
Для новичка простительно использование any, для опытного разработчика - нет. Лучше всего использовать обобщённые типы. Если не очень хочется возится с ними, то используем unknown, который проверяется на месте или же передаётся куда-то в глубь дальше.
Например в случае с глубоким копированием, лично я, не вижу смысла делать эту функцию типобезопасной, потому что туда передать могут реально практически что угодно - в этом смысл, а следовательно и проще / дешевле для разработки использование any будет более чем оправданно
Кто-то ведь копирует данные, значит должен знать как это делать, значит должен располагать информацией о типе копируемого значения, следовательно какая-то информация о типах имеется.
А можно, пожалуйста, пример этого самого "мата в 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
Благо завезли стрелочные функции которые хоть как-то спасают ситуацию.
Если функция не собирается менять коллекцию которую ей передают на вход, то имеет смысл использовать соответствующую аннотацию типа(Sequence вместо list/tuple, Mapping вместо dict, AbstractSet вместо set)
def process_sequence(items: Sequence[...] = ()): # сюда можно передать как list/tuple, так и любую кастомную реализацию Sequence
...
def process_mapping(items: Mapping[..., ...] = {}): # так как мы не планируем менять входные данные, то изменяемый dict как значение по умолчанию не навредит
...
Стоит ещё учитывать, что байткод питона не имеет какой-то спецификации и не обязан иметь обратную совместимость между версиями. Например, в версии питона 3.12 для спискового включения не создаётся отдельный кодовый объект, а включение инлайнится напрямую и в таком случае включение должно быть ещё на долю процента быстрее цикла
Это же просто синтаксис языка, разве нет? Ладно бы вы показали примеры как можно использовать с запечатанные классы с паттерн матчингом, или же комбинацию запечатанных интерфейсов и записей. Но просто показать синтаксическую конструкцию языка и назвать это полезной фичей, это уже слишком.
Про лямбды и varargs(понятия не имею как перевести на русский) даже сказать нечего. С тем же успехом вы могли показать конкатенацию строк и назвать это полезной фичей и, формально, вы даже были бы правы.
Честно, в статье я ожидал увидеть какие-то приколюхи с рантаймом, по большей части, так как синтаксис у жабы довольно минималистичный(за это его и любим). Если уж рассказывать про синтаксические фичи какого-то языка, то в этом плане какой-нибудь тайпскрипт был бы гораздо интереснее.
У 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 файла с последующим получением иконок по захардкоженному пути, слегка нелепо смотрится в отрыве от остальной части тутора. Возможно, стоит присмотреться к пользовательским ресурсам
TL;DR - если лень, то можно использовать unknown, но не any. any - зло!
Клонирование/копирование идеальный случай для обобщённого типа (собственно lodash cloneDeep так и типизирован с использованием generic типа)
Для новичка простительно использование any, для опытного разработчика - нет. Лучше всего использовать обобщённые типы. Если не очень хочется возится с ними, то используем unknown, который проверяется на месте или же передаётся куда-то в глубь дальше.
Кто-то ведь копирует данные, значит должен знать как это делать, значит должен располагать информацией о типе копируемого значения, следовательно какая-то информация о типах имеется.
Действительно типобезопасный debounce можно состряпать как-то так:
В этом примере используется unknown, но я вроде как тут он не нарушает безопасность типов.
А можно, пожалуйста, пример этого самого "мата в 3 этажа" при использовании unknown? Просто мне сложно представить ситуацию, в которой вместо конкретных или обобщённых типов, приходится городить непонятное нечто через any/unknown.
Думаю это хороший подарок для тех кто обучают языку Java других людей. Объяснять новичку что это за
class Main public static void main
немного затруднительно, ведь в процессе обучения до классов и методов доходят далеко не сразу.У некоторых разработчиков кривые руки, раз они забывают сделать await.
Ого, корутину можно запустить с неправильными параметрами... как и обычную функцию... как и всё что угодно.
Асинхронная работа с диском непосредственно в ОС реализована плохо, что каким-то образом является недостатком asyncio по сравнению с синхроным/многопоточным подходом.
У некоторых разработчиков кривые руки, ведь они в асинхронных функциях пишут синхронный time.sleep().
Здесь всё верно. Асинхронность в питоне плохо вплетена в язык. Многие модули со временем получают асинхронные альтернативы (например, subprocesses), но этого мало.
Это действительно так. Разработчики библиотек в принципе всегда страдают, так как им нужно поддерживать кучу платформ, языков, парадигм разработки. Обычные пользователи языка, коих большинство, обычно пишут либо только синхронный код, либо асинхронный с впиливанием многопоточности/многопроцессорности и не страдают от этой проблемы.
Лично я уже слабо себе представляю разработку на питоне без асинхронности, но как уже написали выше:
F1 > Hide copilot
Zig это нечто промежуточное между C и Rust. Zig имеет множество крутых фич как в Rust (tagged unions, optionals, явные ошибки), но при этом имеет более свободную модель выделения памяти как в C (аллокаторы и defer очень удобны, но всё же полностью не защищают от утечек/двойного освобождения).
Если неявный nullability это ошибка на миллиард долларов, то контекст функций в JS это ошибка где-то на 100000 баксов. Это же нисколько не нормально, что сохранив ссылку на, казалось бы, метод объекта, на самом деле мы получаем ссылку на unbound функцию. Вот пример для иллюстрации:
Благо завезли стрелочные функции которые хоть как-то спасают ситуацию.
DEL
А зачем так извращаться? PNG поддерживает текстовые чанки и туда можно запихать любые данные
Если функция не собирается менять коллекцию которую ей передают на вход, то имеет смысл использовать соответствующую аннотацию типа(Sequence вместо list/tuple, Mapping вместо dict, AbstractSet вместо set)
Стоит ещё учитывать, что байткод питона не имеет какой-то спецификации и не обязан иметь обратную совместимость между версиями. Например, в версии питона 3.12 для спискового включения не создаётся отдельный кодовый объект, а включение инлайнится напрямую и в таком случае включение должно быть ещё на долю процента быстрее цикла
Это же просто синтаксис языка, разве нет? Ладно бы вы показали примеры как можно использовать с запечатанные классы с паттерн матчингом, или же комбинацию запечатанных интерфейсов и записей. Но просто показать синтаксическую конструкцию языка и назвать это полезной фичей, это уже слишком.
Про лямбды и varargs(понятия не имею как перевести на русский) даже сказать нечего. С тем же успехом вы могли показать конкатенацию строк и назвать это полезной фичей и, формально, вы даже были бы правы.
Честно, в статье я ожидал увидеть какие-то приколюхи с рантаймом, по большей части, так как синтаксис у жабы довольно минималистичный(за это его и любим). Если уж рассказывать про синтаксические фичи какого-то языка, то в этом плане какой-нибудь тайпскрипт был бы гораздо интереснее.
А так и нельзя писать
Это не так.
result.scalars()
привязан к соединению из пула и к сессии,result.scalars().all()
нет. В теории, это может вызвать проблемы при формировании ответа на запрос, ведь сессия в данный момент уже будет закрыта.stream_scalars
по большей части нужен для работы с большим объёмом данных. Если данных немного, то лучше сразу забрать их все черезscalars().all()
.async for
служит для других целей. С синхронным итератором вы тоже можете выполнять асинхронные операции в процессе итерации.Так и должна работать любая мультиплеерная игра с элементом соревновательности. Клиенту доверять нельзя, это же база
Руки бы поотрывать тому, кто меняет сигнатуру публичной функции без сохранения обратной совместимости. И это безотносительно ЯП.
Здесь я запутался в понятиях метакласс и класс. Но я имел ввиду то, что наследники класса type(то есть, метаклассы), это самые обычные классы, type никакой магии не делает.
Формально, Path можно назвать метаклассом. Но чаще всего под метаклассами всё же имеют ввиду наследников класса type.
Но да, если рассматривать Path в таком ключе, то вы правы.
Откуда это взялось? Или
я_точно_синглетон = dict()
тоже синглетон? Класс это экземпляр класса type или одного из его наследников, не более.type ничего не добавляет своим наследникам. Единственное что type.new делает, так это создаёт новый экземпляр переданного класса и заполняет его словарь, используя переданные параметры. Ну и валидацию выполняет, чтобы от некоторых типов не наследовались, но это не суть важно.
Это плохой пример использования метаклассов. Как вы уже и написали, миксины для этого подойдут гораздо лучше. Конечно, не стоит специально избегать создание метаклассов, но и городить их почём зря, это тоже не лучшая практика.
Действительно хорошим примером метакласса может послужить Enum из стандартной библиотеки.
Насколько я знаю, pathlib никаким образом не использует метаклассы. Поправьте, если я ошибаюсь. И даже если использует, то зачем в теории может понадобиться переопределять div на каком-нибудь PathType, а не на самом Path?
Очень крутой материал. Но вот загрузка предметов ручками из JSON файла с последующим получением иконок по захардкоженному пути, слегка нелепо смотрится в отрыве от остальной части тутора. Возможно, стоит присмотреться к пользовательским ресурсам