Как стать автором
Поиск
Написать публикацию
Обновить

Комментарии 49

Типизируйте как можно раньше.

А лучше сразу пишите на статически типизированных языках

Как статически типизированный язык поможет в случае джейсона из стороннего источника, стесняюсь спросить?

Кроме того, ценность статической типизации не абсолютна, в очень многих случаях она просто мешает.

В домене (в бизнес логике) не должно быть словарей.

Разве что локально внутри одной функции: видно глазами его структуру.

JSON должен быть представлен в типизированный формат и валидирован (или тип должен принимать только валидные значения) чтобы быть передан в домен.

Иногда динамическая типизация мешает. Но с учётом вышесказанного

В домене не должно быть словарей.

Ну расскажите это создателям эрланга. Там кроме словарей — других составных типов данных всего два: кортеж и связный список.

Я по неопытности первое время в эликсире тащил повсюду структуры (которые суть словари с ограниченным набором ключей), а потом понял, что это вообще никому не нужно.

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

403 ОШИБКА

Просьба не может быть удовлетворена.

Дистрибутив Amazon CloudFront настроен на блокировку доступа из вашей страны

Ну ок, прочитал

immutable_types  =  set (( int ,  str ))

класс  Frozen ( объект ) 
    : def  __init__ ( self ,  value ) 
        : self._value = value  

    def  __getattribute__ ( self ,  name ): 
        если  name  ==  '_value' :  return  super ( Frozen ,  self ) . __getattribute__ ( name ) 
        v  =  getattr ( self . _value ,  name ) 
        return  v  если  v . __class__  в  immutable_types  иначе  freeze ( v )
  
    def  __setattr__ ( self ,  name ,  value ): 
        if  name  ==  ' _value ' :  super ( Frozen ,  self ) . __setattr__ ( name ,  value ) 
        else :  raise  Exception ( "Невозможно изменить замороженный объект {0}" . format ( self._value ))
    
def  freeze ( value ): 
  return  Frozen ( value )

Словари только на чтение и чтобы IDE понимал из структуру было бы неплохо.

Вроде, есть библиотеки, но всё равно надо определить структуру отдельно

from types import MappingProxyType
from typing import TypedDict

class UserData(TypedDict):
    name: str
    age: int
    active: bool

_data: UserData = {
    'name': 'John Doe',
    'age': 30,
    'active': True
}

USER_DATA = MappingProxyType(_data)

А ещё лучше

// JavaScript - работает без отдельного объявления
const CONFIG = { name: "Vasya", is_active: true, level: 3 };
console.log(CONFIG.name);

чтобы IDE понимал из структуру

Зачем?

Но прям если очень надо, то вот PEP-589, вот Language Server, а вот инструкция, как прикрутить в neovim.

Мешает чему? Возможности писать не типобезопасный код? Лени разработчика?

Поддерживаемости и расширяемости. Хорошему разработчику типы не нужны, а плохому — не помогут.

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

  • использование аннотаций в связке с тайп-чекерами типа mypy позволяют избежать множества ошибок типизации.

  • Даже хорошие программисты могут допускать ошибки :) а Ваш тезис что-то сродни «хорошие программисты пишут код без багов»

  • Я уже молчу про тормознутость буквально всех динамически-типизированных языков (надеюсь, не придется объяснять как именно динамическая типизация негативно аффектит производительность)

P.S. За последние лет так 5 я видел ровно 0 проектов на Пайтоне, где type-annotations были бы опциональными/не использовались вовсе: везде они были обязательными + везде mypy / pyright. Другое дело, что огромное множество Python-программеров не умеют в типы и попросту не хотят с этим разбираться, что вырождается в конечном итоге в г*вно-код c повсеместными Any и т.д. Но тут, опять же, есть решение: хорошо настроенный тайп-чекер бьет за такое по рукам :)

Вы ведь в курсе, да, что питон — динамически типизирован, а аннотации типов прикручены сбоку и вообще никак в результате на исполняемый код не влияют, да?

не придется объяснять как именно динамическая типизация негативно аффектит производительность

Я не знаю, что такое «аффектить», но если имелось в виду «влиять на» — то никак. В рантайме хаскеля (и подавляющего большинства строго типизированных языков) — никаких типов нет из-за type erasure, которую придумали как раз ради повышения производительности.

Ну и по мелочи:

аннотации типов (в том же пайтоне) позволяют сделать код более явным и прозрачным

По сравнению с чем? С питоном без аннотаций? — Возможно. При этом скала насквозь типизирована, но всё еще порождает анекдоты типа такого:

Скрытый текст

Докладчик на конференции показывает с экрана код на скале, спрашивает: «Достаточно ясно, или увеличить шрифт?»

В аудитории неразборчивый ропот. Докладчик увеличивает шрифт.
— Так понятнее?

Голос из зала:
— Нет, это по-прежнему код на скале.

использование аннотаций в связке с тайп-чекерами типа mypy позволяют избежать множества ошибок типизации

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

Даже хорошие программисты могут допускать ошибки

Конечно. Но обычно не с типами (я что-то никогда не видел вызов функции store_user(product) или типа того за всю свою 30-летнюю карьеру), зато ошибку четности, или off-by-one, или плюс вместо минуса — а именно такие ошибки самые частые — никакие типы, кроме зависимых, не отловят (но завтипов нет даже в хаскеле, а идрис — наверное никогда уже, к сожалению, не станет production-ready.)

На скольких языках, кроме питона, вам доводилось писать продакшн-код? Потому что если сравнивать просто питон и питон с mypy — то это не про типы, а про общую импотенцию языка.

Вы ведь в курсе, да, что питон — динамически типизирован, а аннотации типов прикручены сбоку и вообще никак в результате на исполняемый код не влияют, да?

в курсе, конечно. Мне даже как-то обидно, что вы подумали, что я могу быть не в курсе :)

Я не знаю, что такое «аффектить», но если имелось в виду «влиять на» — то никак. В рантайме хаскеля (и подавляющего большинства строго типизированных языков) — никаких типов нет из-за type erasure, которую придумали как раз ради повышения производительности.

Я про то, что вычисление типа переменной в рантайме и выделение места на стеке под ее хранение - это runtime-overhead.

По сравнению с чем? С питоном без аннотаций? — Возможно. При этом скала насквозь типизирована, но всё еще порождает анекдоты типа такого:

да, по сравнению с Питоном без аннотаций. Код на Скале (лично для меня) - "темный лес", но связано это с тем, что я не силен в ФП, а не с тем, что Скала - статически типизированный язык

Конечно. Но обычно не с типами

Одна из достаточно частных проблем в Python - это когда функция возвращает, например, Optional[T], а программист использует ее результат, закладываясь на то, что там будет T и только T.
Но проблема Пайтона в том, что эта потенциальная проблема в некорректном использовании такой функции может быть идентифицирована только через тайп-чекер, либо же, она проявит себя через N лет и попросту свалит прод :)

Возможно, я изначально не совсем верно понял Ваш посыл, но лично я говорил сугубо в контексте Пайтона, и да, я подсознательно сравнивал Пайтон с тайп-аннотациями и без оных.
В целом, если сравнивать различные ЯП, то мой опыт позволяет лишь сравнить пайтон с растом, но не более того

Я про то, что вычисление типа переменной в рантайме и выделение места на стеке под ее хранение — это runtime-overhead.

Еще раз: с тайпчекером, или без, — в рантайме код одинаковый. Ну и весь мой опыт показывает, что бутылочное горлышко всегда закопано в пользовательском коде, а не в оверхеде из-за неоптимального выделения места на стеке языком. Всегда.

функция возвращает, например, Optional[T], а программист использует ее результат, закладываясь на то, что там будет T и только T.

Ну не знаю, я и без типов проверю на None практически всегда.

Еще раз: с тайпчекером, или без, — в рантайме код одинаковый.

не совсем понятно, причем тут "тайпчекер", если речь про статическую / динамическую типизацию в целом? В статически-типизированных языках информация о типах переменных (соответственно и об их размере на стэке) известна на этапе компиляции, в динамически-типизированных языках - эта информация вычисляется в рантайме.


Аргумент про "бутылочное горлышко" мне тоже не очень понятен: я могу сколько угодно г*вно-кодить на расте или писать около-идеальный, идеоматически верный код на пайтоне с кучей оптимизаций, в 99.9 % код на расте будет во множество раз быстрее

Аргумент про бутылочное горлышко, очевидно, работает только в рамках одного языка.

Но не могу не напомнить, что создатели языка Раст не смогли на собственноручном созданном языке написать проект, ради которого язык и был создан. Так что физзбазз на нем — юудет, очевидно — быстрее, а для чего-то серьезного я бы поостерегся его выбирать.

Аргумент про бутылочное горлышко, очевидно, работает только в рамках одного языка.

Но не могу не напомнить, что создатели языка Раст не смогли на собственноручном созданном языке написать проект, ради которого язык и был создан. Так что физзбазз на нем — будет, очевидно — быстрее, но для чего-то серьезного я бы поостерегся его выбирать.

Раст был приведен сугубо для примера :) Вместо раста я мог бы привести Go/Java/C# - смысл бы от этого не изменился

Я не знаю, что такое «аффектить», но если имелось в виду «влиять на» — то никак. В рантайме хаскеля (и подавляющего большинства строго типизированных языков) — никаких типов нет из-за type erasure, которую придумали как раз ради повышения производительности.

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

Если у вас написано a + b, и в вашем языке + может означать сложение и intов, и doubleов, и строк, и shortов каких-нибудь, и тензоров из вашей любимой библиотеки, то вам надо будет в рантайме проверять конкретную метку a, конкретную метку b, иметь if с логикой для конвертации в какой-то общий тип, и только потом суммировать. Компиляция с типами же всё это делает в компилтайме, и в рантайме остаётся только условный ассемблерный addword или call stringConcat. Более того, если это происходит в компилтайме, то открываются возможности для оптимизации после такого инлайнинга.

И именно поэтому когда в хаскеле типы таки не стираются (например, компилятор не мономорфизирует полиморфную функцию с типом Num a =>, вместо этого таская набор указателей на функции, коим является словарь Num a под капотом, или у вас там экзистенциальный тип затесался где), то всё это может работать медленнее на порядок-полтора.

В моём языке a + b может обозначать только арифметическое сложение, а тип «тензор» невозможен, как и любые другие пользовательские типы.

А числа в вашем языке тоже всего одного типа рантайм-представления, как в JS, или там есть отдельно целые, отдельно с плавающей точкой, и так далее?

И что будет, если в a окажется, скажем, строка? Рантайм пойдёт, возьмёт первые 4-8 байт её представления, проинтерпретирует как число, и сложит с b? Или произойдёт что-то другое?

Ну и да, тут речь изначально о питоне шла, а там такой рантайм-диспатч по меткам есть точно, и тут уже надо уточнять, обсуждаем мы все динамически типизированные языки, или некоторые.

Да есть, конечно, рантайм-диспатч, куда без него.

Словари это лучшее, что есть в питоне. Просто нужно уметь их готовить.

Словари должны существовать локально. В пределах одного экрана словарь заполнили и использовали.

А если ХЗ где искать кто его заполнил в 10 изменил и что там вообще может быть то это караул

Мне всегда смешно читать про предложения обернуть нормальный тип данных, типа словаря, в триста хрупких слоёв абстракции, при том, что в эрланге (и в эликсире), например, типов данных сложнее словаря (map) просто нет, и это вообще никому не мешает писать код, про который во внешнем мире известно обычно то, что он — fault tolerant по определению.

Ну это уже демагогия какая-то. Как именно используются мапы в проектах, что вы упоминаете? За счет чего достигается fault tolerant упоминаемого вами кода? Пока что вы просто выражаете несогласие, но не пытаетесь его аргументировать

Как именно используются мапы в проектах, что вы упоминаете?

Наравне с кортежами и связными списками — как единственный способ представления данных.

За счет чего достигается fault tolerancy упоминаемого вами кода?

Я наивно полагал, что диссертацию Джо Армстронга прочитали все люди, мнящие себя разработчиками (ну, или хотя бы слышали о ней краем уха). https://nodramadevops.com/2019/04/joe-armstrong-and-fault-tolerant-concurrent-systems/

  1. Используем mypy

  2. Не используем cast

  3. Кривые диеты больше не получается использовать

И все, проблема решена. Точнее её больше нет ибо ей неоткуда взяться

Проекты на python в целом не очень поддерживаются, да и среднее качество кода оставляет желать лучшего. Если вы пишите что то больше чем на 1000 строк кода, просто не делайте это на питоне. Это язык для студентов и дев опсов. В остальных случаях лучше его не использовать. Да на чем угодно можно писать качественный код но на python это гораздо сложнее.

Ну так дело не в Python. Я сейчас в один чужой проект влился - там на C# тоже самое: json и dictionary. Иногда даже вложенные.

Говнокодить можно на любом языке.

Но на динамически типизированных это особенно мягко и приятно. Пока санитары не пришли.

Бред полный, я работаю с проектом где множество сервисов, десятки тысяч строк кода на питоне. И все прекрасно поддерживается, потому что команда нормальная.

В хранилищах пар ключ-значение аннотируйте словари как мэппинги

этот совет выглядит скорее вредным, потому что typing `dict[str, str]` буквально и описывает hashmap неопределенной длины, но однородного содержания.

Подводные камни прячутся в гибкости. Мы можем описать коллекцию книг как:

all_books: list[Book] = []

А можем как хэшмапу с быстрым доступом по выбранному свойству:

books_from_names: dict[str, Book] = {book.name: book for book in all_books}

Но гибкость нам позволяет и сам Book описать как словарь:

dict_book = {
  "name": "Azbuka",
  "pages": 33,
  "weight": 444.4,
  "category": Category.HORROR,
}

Тем не менее легко выделить критерии, по которым легко признать, что у вас на руках Object:

  • Значения, вероятно, неоднородны (хотя легко придумать описание книги, подходящее под `dict[str, str]`)

  • Ключи - это не свойство значения, доступного по ключу, это свойство Object.

  • Длина словаря определена набором свойств описываемого Object

Таким образом, два последних пункта буквально сигнализируют о том что мы объект и его поля запихали в хэшмапу.

И такое можно делать - но желательно осознанно. Иначе по коду могут начать летать словари словарей, примерно с таким тайпингом:

dict[str | int, dict[str, dict | str] | float | str | int]

В качестве же компенсации за критику присоединюсь к рекомендации причинять pydantic. Начиная с V2 он вырос на голову относительно себя:

  • Написан на rust (В смысле, прибавил в производительности)

  • Через Annotated можно делать крутяцкие валидации-сериализации и переиспользовать их

  • Дженерики начали работать как дженерики (в отличие от батьки-питона) - в том числе и с предыдущим пунктом.

  • Умеет включать в свои модели данных датаклассы и восстанавливать их при парсинге json

  • Имеет TypeAdapter, который позволить спарсить из json в том числе чистый dataclass, такое вот содружество.

  • Наверняка я что-то ещё интересного упустил, но и так вроде неплохо звучит.

Одна проблема остаётся:

Словари, которые можно сформировать/ изменить в разных частях программы это write only code. То есть MyPy может что-то отловить, но человек не может.

Дикты инварианты, потому что мутабельны. Тип Mapping же ковариантный. К тому же, с аннотацией Mapping я могу подсунуть как dict, так и frozendict.

Может кто знает, появилось ли что-то кроссплатформенное, чтобы dto между сервисами в принципе не превращать в json? Что-то вроде protobuf+grpc, но не так ублюдочно сделанное, как последний под python, где вместо фичей языка используется доступ к гландам через ж, что пользоваться этим куском неприятно и хочется мыть руки постоянно. Чтобы какой-то объект из api доставать и сразу с ним работать, без десереализаций и маппингов.

Ну и естественно, чтобы между Java/C#/Go/Python/Rust/JS как влитое интеропалось

Есть такое?

Подскажите плз. Если мне из сети прилетает от 500 json/sec до 5000 json/sec и нет времени на сериализацию в объект и обратно. И актуальность данных примерно 0.5 секунды(если из очереди данные придут с задержкой 1 сек то это уже тухлые данные) что делать? Переходить на C? :) есть подозрения что мой код не успевает за данными

У меня кластер из трёх машин на эликсире хавает до 80К/сек в пике, десериализует, трансформирует с некоторой [несложной] математикой и сериализует обратно с latency не выходящей за рамки 100ms примерно.

Я python с трудом освоил, посмотрел в вики на elixir - выглядит не читабельно, максимально интуитивно не понятно :))) . Если знаете какую-то хорошую документацию по elixir, буду благодарен ссылке. Вряд-ли освою но хотя бы представление буду иметь.

Ещё уточню, 100 ms это на обработку(десериализация, математика, сериализация) примерно 5к-8к json сообщений? Для одного сообщения это явно многовато просто :)

Это 99-й перцентиль latency от входа до выхода сообщения.

Переходить на C?

Для такого Golang есть. Прост (слишком), оптимизируем, компилируем. Python из-за своей специфики уж очень сложно оптимизировать.

Словарь - это (англ)Map? Я не прикалываюсь, просто не попадалось такое употребление. Обычно (у нас) говорят "карта", "мэпа", изредка - "отображение".

  • По поводу изменяемости словарей: существует immutable аналог dict - frozendict. К слову, pydantic-модели, как и дэйтаклассы, по умолчанию тоже мутабельные.

  • Если вам действительно нужна валидация - pydantic-модели это решение. Но, в реальности, валидация не всегда нужна, и тогда pydantic-модели лишь привносят overhead на доп. сериализацию/десериализацию данных (да-да, даже в версии 2 pydantic достаточно тормознутый)

Зарегистрируйтесь на Хабре, чтобы оставить комментарий