Как стать автором
Обновить

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

from enum import Enum
import json

class UserStatus(str, Enum):
    PENDING = 'Pending'
    ACTIVE = 'Active'
    INACTIVE = 'Inactive'
    DELETED = 'Deleted'

print(UserStatus.PENDING.value)
print(json.dumps(UserStatus.PENDING)) 

enum в Python все же более гибкие чем показано в статье

Разве это не присвоит просто каждому члену перечисления по строке?


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

Да, вот этот пример, вроде бы, то что надо

В вариантах перечисления может быть разный набор полей? В Rust можно создать такое перечисление:


enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

Да, может


from dataclasses import dataclass
from enum import Enum

@dataclass
class Pending:
    s: str

@dataclass
class Active:
    a: int
    b: str

class UserStatus(Pending, Active, Enum):
    PENDING = Pending("Pending")
    ACTIVE = Active(42, "Active")

Будет ли в такой схеме Pending("Foo") инстансом UserStatus?

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

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

А почему вы решили что связывание в данном контексте обязательно наследование, а не, например, композиция?

Можете вместо наследования использовать что угодно, лишь бы Pending("Foo") имело хоть какое-нибудь отношение к UserStatus.


Связывание в данном контексте означает возможность связать значение перечисления с произвольном значением, а не с константой. То есть Pending("Pending"), Pending("Foo") и Pending("Bar") должны быть полностью равноправны как значения перечисления, чего в коде выше не наблюдается.

Комбинируя увиденное в других комментариях, могу предположить как это должно выглядеть на Питоне на самом деле:


@dataclass
class Quit:
    pass

@dataclass
class Move:
    x: int
    y: int

@dataclass
class Write:
    value: str

@dataclass
class ChangeColor:
    r: int
    g: int
    b: int

Message = Union[Quit, Move, Write, ChangeColor]

Если только я не допустил какой-нибудь глупой ошибки — это и есть аналог перечислению из Rust. А вовсе не та ерунда, которую пытались написать cbmw и menstenebris...

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


from typing import Union
class SomeClass:
    message: Union[Quit, Move, Write, ChangeColor] = None

и обозначает что у поля SomeClass.message ожидаемые(!) типы значений — это перечисленная четвёрка.
Но это вовсе не мешает присвоить туда что угодно


a = SomeClass()
a.message = Write("Test line")
a.message = ("Nope", "still", "can", "assign", "anything", True)

А полного прямого аналога предложенного исходно перечисления я в Питоне вот так и не назову. Всё-таки "подсказка по типам" Питона принципиально отличается от жёсткого задания типов Раста, у них даже задачи разные.

Всё так, Union не делает проверок в runtime.
Если совсем придираться, на мой взгляд, должно быть как-то так:


from typing import Optional, Union
class SomeClass:
    message: Optional[Union[Quit, Move, Write, ChangeColor]] = None

Да, как вариант.

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

А что именно там неудобно будет?


message: Union[Quit, Move, Write, ChangeColor] = None

Э-э-э, а None-то тут зачем и откуда?

Э-э-э, а None-то тут зачем и откуда?

Да просто чтобы выставить значение-по-умолчанию "значение не выставлено". Считать ли это антипаттёрном — вопрос сильно дискуссионный, но именно в таком значение None часто используется.

В python я могу перечислению присвоить не только строку, но и dataclass с полями. Таким образом каждый член перечисления становится структурой с полям.
from dataclasses import dataclass
from enum import Enum

@dataclass
class Status:
    s: str

class UserStatus(Status, Enum):
    PENDING = Status("Pending")
    ACTIVE = Status("Active")

print(UserStatus.PENDING.value)

Проблема в другом. В python перечисление это костыль над системой классов, им даже пришлось отдельную реализацию писать чтобы он не жрал столько памяти. В Rust же перечисление это абстракция нулевой стоимости, можно использовать по поводу и без. Но из за этого кое какие вещи становятся недоступны, например в diesel не могут нормально скрутить вместе enum в rust и enum в postgres.

Могут ли варианты перечисления иметь данные разных типов?

Будет ли в такой схеме Status("Foo") инстансом UserStatus?

Простите, а зачем? То есть "для какой цели/задачи"
Потому что я не уверен, что понимаю суть Вашего вопроса. По структуре классов Status является базовым классом для UserStatus и не может быть инстансом унаследованного класса, тем более через множественное наследование.
И мне не совсем понятно зачем там вообще использовано то множественное наследование, enum.Enum этого не требует. С тем же успехом работать будет и "class UserStatus(Enum):"

Потому что перечисления в Rust (которые на самом деле не перечисления) работают именно так, а тут вроде как пытаются построить их аналог (который и не аналог вовсе)...

Хмм.
В этом случае сама идея "сделать из питона раст" видится мне наибольшая ошибка — ибо конечно же результат не будет столько же удобен/гибок/удобочитаем, как исходный Rust! Из раста если питон делать тоже будет непойми что.

Никто аналоги тут не строит, вся эта ветка обсуждения началась с того что мы в статье действительно показали python enum немного однобоко (не такими гибкими).
А к вопросу о том как работают перечисления в расте, вот вам небольшой контрпример:


struct Move(i32, i32);

enum Message {
    Quit,
    Move(Move)
}

Какое отношение имеет структура Move к варианту Message::Move?

Она является типом первого и единственного поля Message::Move, а что?

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Конечно, Рамблер делала ужасные вещи, но разве мы должны ставить минусы под постами, которые вообще никакого отношения к тому инциденту? Ведь бизнес хотели отжать одни люди, а писали статью другие. Так значит мы должны принижать труд вторых?

НЛО прилетело и опубликовало эту надпись здесь
А еще хабр корпоративный блог не закрыл. Границы ответсвенности то должны быть.

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

Гитлер_хороший_художник?
Ну тащемта да.
Возможно, музыкант был грубияном, наркоманом и мерзким чуваком, но если музыка у него хорошая — разве нельзя признать, что она хорошая?
Рамблер большой, он делал плохие вещи, делал хорошие вещи, в нем работает куча людей.
Какой уровень взаимодействия с Рабмлером делает тебя соучастником наезда на автора NGinx?
НЛО прилетело и опубликовало эту надпись здесь
Тогда вам стоит пройти по всем площадкам, где публикуются работы Google, Microsoft и прочих гигантов тоже с подобными заявлениями — и не забудьте заминусить ВСЕ их проекты и статьи, а то воротят бог весть что. Минусы под статьи ставят к статье, а не к людям или компаниям — иначе нарушается объективное восприятие информации, которая была в статье подана.
Только наивный будет считать, что бизнес будет всегда чистеньким, особенно больших размеров — везде есть люди со своими интересами, которые не всегда благие для остальных. Но судить по паре десятков людей, решивших попиариться или что-то хуже сделать о тысячах людей, которые любят свою работу и/или просто пишут тут статьи — как воспринимать мир черно-белым. Принцип коллективной отвественности не стоит тут навязывать, да и никто не предложит ВСЕМ этим тысячам сотрудников достойное место чисто из солидарности, а у кого-то ипотека, дети…
Удобно быть моралистом, когда лично тебя это никак не касается — можно встать в атакующую позу и не считаться с людьми, потому что вы правы в своем праведном гневе) И далеко не все сотрудники Рамблера были в восторге — поищите публичные письма их сотрудников, вроде были как раз тут, на хабре.
Так никто не говорит людям «уходите из Рамблера» или «не печатайте статьи на Хабре»! Достаточно просто не печатать в их корпоративном блоге. Или за это там тоже наказывают? ))
Ну так давайте не будем смотреть конференции и прочие мероприятия Google?) Они ведь пиарятся, хотя давно убрали «Don't be evil» из своего устава, да и сколько было скандалов (что неизбежно при увеличении размеров фирмы).
Все равно мы будем пользоваться Android, писать на Go и прочее-прочее).
Имидж компании состоит не только из негативных поступков, но и из положительных. Лично я не одобряю заведение уголовного дела, но и продолжать бессмысленное макание людей (которых причисляют косвенно к виновникам) в грязь не вижу смысла. Имиджевые потери для Рамблера и так были достаточно высокие — пусть отрабатывают, как могут. Даже если это простые статьи тут, которые кому-то пригодятся.
И если капнуть чуть глубже в те события: подавляющее в тот момент было именно пожелание «уходите из Рамблера».
И давайте размышлять абстрактно — кто-то обрушил своим недальновидным поведением имидж фирмы, причем осудил другого человека. Что будет дальше? Фирма должна развалиться из-за того, что теперь ее имидж плохой? А чем это отличается от «уходите из Рамблера» по причине того, что он перестанет существовать?)) Вариант «не чинить репутацию» равен «уйти из Рамблера» на длинной дистанции, просто не по своему желанию)
Да, ради этих слов стоило зарегиться!
Вариант «не чинить репутацию» равен «уйти из Рамблера» на длинной дистанции
Разработчику важно работать на свою репутацию, тогда ничего не страшно. К тому же на длинной дистанции все уходят.
Давно сидел тут без учетки, но после N-ого раза на тему злых корпораций прорвало)
Согласен про работу на свою репутацию. Про уход — все хотят уходить из компаний с нормальной репутацией, а не оставаться мечеными. Пускай исправляются полезными для общества делами.
Осуждать программиста за его статью по вине какого-то менеджера (которого он и никогда не видел/слышал скорее всего в наших реалиях) есть дело не самое адекватное. Какой срок давности у данного преступления? Или Рамблер уже никогда не отмоется?))
Всегда интересно, как это на хабре (все же адекватные, уважают чужое мнение) ставят минус в карму без явной аргументации в комментариях — узнал)

Минус и за просто вопросы или цитирование исторических фактов могут поставить. Молча ибо "сам догадайся чем ты мимопроходившему сообществу не мил". Особенно когда кроме минуса ответить то и нечего ибо документальный факт, а чью-то мозоль отдавил или картину мира пошатнул.
Так что — остаётся относиться к хабру как к просто месту общения просто случайных людей. Ничем, по факту, айтишники не отличаются от кого угодно стереотипно другого. И хабр тут не исключение.

НЛО прилетело и опубликовало эту надпись здесь
Тут запрет на личное мнение по данной ситуации?)
Попытка дискредитации через сопричастность к фирме (а не к решению по данной проблеме) — это «сильный» аргумент. Напишешь хоть слово — сразу враг?) С подобной нетерпимостью даже диалог не выстроишь.
Я не просил поддерживать Рамблер. Не писал о том, что невиновата организация — только люди, которые управляют (которые уже почти все свалили или будут заменены). Взваливать ответственность на сотрудников — как вспоминать немцев из ВОВ и вешать их вину на современников; как запрещать олимпийцам выступать под флагом страны, в которой они родились и любят (страну, а не государство). У всех должен быть шанс исправиться, да и не отвечают сыны за отцов.
И если мы перешли на личности — у вас у самого карма далеко внизу, что намекает на не самый адекватный способ ведения диалога самим обществом. Да и классификация про клоунов заочно в профиле — это вряд ли нормально. С чужим мнением принято считаться, даже если оно вас не устраивает — априори. Вы сами об этом просите в профиле) Более того — вы пытаетесь задавить не конкретные действия, а чувства других людей: кому было все равно, тот уже ушел на волне «как ты можешь там работать».
НЛО прилетело и опубликовало эту надпись здесь
Это лишь обозначение вашей ангажированности.

Я не стал скрывать свою личность, хотя и мог.
Ваш пример изначально некорректный.

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

И зря — слово режет сильнее ножа. Для себя вы просите корректности в выборе слов.
Не у всех, а только у тех кто признал ошибку.

Где вы увидели НЕ признание ошибки?) Ошибка есть — это факт, о чем мы уже писали ранее.
Вы читали чем кончилось дело? Дело передали в компанию, которая продолжила судебное преследование уже в международном формате. Заслуживает ли права на исправление тот, кто только делает вид что исправился?

Если вы настолько в курсе, то стоит углубиться еще больше в последние новости — Мамут больше не у руля, виновник самоудалился (при этом попортив кровь Сберу, который ни сном-ни духом об этом был). Да, разбирательство продолжается, и всем очевидно, что оно закончится в пользу автора — проще в суде заткнуть одну российскую компанию по неправомерной претензии, чем заставить весь мир платить за софт с оглядкой на 15 лет использования.
проще в суде заткнуть одну российскую компанию по неправомерной претензии, чем заставить весь мир платить за софт с оглядкой на 15 лет использования
А-а… так вы считаете, что весь мир должен платить Рамблеру???
Ни в коем случае. Вы ведь сами отвечаете на свой вопрос цитированием меня.
неправомерной претензии

В итоге недальновидной и жадной (и несколько жалкой на фоне сделки F5 Networks по Nginx) попытки нагреться высшего руководства перед уходом сильно пострадал имидж фирмы — а также был причинен ущерб автору. О какой выплате по авторским правам может идти речь, если все понимают, что доказательств нет?
По поводу части с pattern-matching:
Так как указано в статье в Python обычно не пишут. Python-разработчик написал бы что-то такое:
def serialize(user_status: UserStatus) -> str:
    mapping = {
        UserStatus.PENDING: 'Pending',
        UserStatus.ACTIVE: 'Active',
        UserStatus.INACTIVE: 'Inactive',
        UserStatus.DELETED: 'Deleted',
    }
    return mapping[user_status]


Соотвественно если вдруг в функцию будет передано значение, которого нет в UserStatus — будет исключение.
И создал бы дополнительный словарь в памяти, который находится в куче, а не в стеке и обращение к нему будет дольше, не говоря уже про вычисление хэш-функции на ровном месте. А главное непонятно зачем, все инструменты для получения строки из enum есть в стандартной библиотеке.
Сейчас в python 3.10 pattern matching подвезут — станет полегче.
На питоне так и должно быть.
Как должно быть? должна быть написана лишняя функция дублирующая функционал из стандартной библиотеки? или нужно объявить недостатки языка его достоинствами?
По моему, по питоновски в таких случаях нужно просто использовать словарь. Без классов и функций.
Словарь это не единственная структура данных. Каждая структура данных для своей задачи. И все многообразие структур данных даже в python весьма полезно, нужно только понимать сильные и слабые стороны. Или вы всерьёз думаете что в других языках нет хэш таблиц и это разработчики на python такие умные?
Чем перечисление лучше словаря?
Экземпляр перечисления является самим перечислением. В результате чего нормально начинает работать типизация и исчезают ошибки с опечатками в ключах словаря, просто потому что IDE наконец понимает что там должно быть, а чего нет. Плюс перечисление невозможно случайно изменить в процессе работы программы и не нужно инициализировать.
Я не против перечислений. Я против того, чтобы из динамического языка делать статический. Ну и против того, чтобы на динамическом языке писать так же как и на статическом.
Нужно ясно понимать области применения и преимущества каждого типа языков и:
— Выбирать язык сознательно, ориентируясь на его плюсы.
— Стиль программирования должен быть тоже разный.
А то я насмотрелся на то, как люди приходят в питон из джавы и начинают лепить скажем геттеры и сеттеры, ну или посмотрят на модные веяния и давай тащить в питон статическую типизацию.
Так потому она и опциональная, что язык динамический. Хотите — тащите, не хотите — не тащите. Мне вот удобно, что IDE может правильно подсказки выдавать, удобно, что видно, что из себя представляют аргументы функций.
Это добавляет много ненужного шума в текст. А тексты приходится читать.
Не знаю, что именно вы называете шумом. Но знать, какой тип у аргумента — это очень даже удобно.

def get_user_by_uuid(user_uuid):
    pass

def get_user_by_uuid(user_uuid: UUID):
    pass

UUID здесь спокойно мог бы быть и обычной строкой.
Здесь фактически комментарий вынесен в синтаксис, что ИМХО есть не очень хорошо.
Когда мне кажется это полезным и нужным, я указываю тип аргумента в комментариях. Но в большинстве случаев это вообще не нужно. Тем более, что не обязательно всегда передавать один и тот же тип аргумента в функцию.
Автоматическая генерация документации будет доступна только при очень определенном типе форматирования комментария. Кроме того, комментарий будет дублировать саму переменную.
Пример простой, а на деле часто бывают более интересные варианты, где объект будет dataclass'ом, где один из аттрибутов будет enum'ом и так далее. IDE на каждый уровень будет показывать подсказки и типы. Я понимаю, если у вас какой-то pet-проект, где кроме вас никого, но если это библиотека и у нее есть публичный api, то почему бы не облегчить жизнь тем, кто будет ею пользоваться?
Это всё достаточно субъективно.
Мне, например, очень не нравится кадый раз обращаться к документации (при условии что она есть) чтобы понять какие типы данных я могу передать в функцию. Это скорее дело привычки и определяется личными приоритетами разработчика.

Так то и вынесение куска метода в другой метод можно обозвать как "ненужный шум в тексте".

Иногда не только можно, но и нужно! :)
Ты просто не пробовал новые языки со статической типизацией. Алгоритм Хиндли — Милнера позволяет тебе не указывать тип на каждый чих. Например
use std::collections::HashMap;

fn main() {
    let mut d = HashMap::new();
    d.insert("key", "value");
    let v = vec![1, 2, 3];
    let t = (d, "world");
}

Но, справедливости ради, rust все равно когнитивно сложнее.
Тогда лучше уж C# рекомендуйте
Zero cost abstractions — это про стоимость для производительности, а не для мозга.
4 типа указателей, 2 строки (Я про срез), на первых порах — войны с borrow checker-ом, операции над срезами
Впрочем rust все равно няшка, что тут говорить
Нужно ясно понимать области применения и преимущества каждого типа языков и:
— Выбирать язык сознательно, ориентируясь на его плюсы.

А какие, по вашему, плюсы у динамической типизации?

Скорость написания скрипта, например. Если объект поддерживает некое производимое над ним действие — скрипт просто работает; если нет — выбрасывается исключение.
И если нужно — то в Питоне объекту возможность нужное действие производить можно и на лету добавить.


Или переиспользовать одно имя переменной под разные типы объектов в рамках одного метода.
dict((v: k) for k, v in some_entities.items()) без разницы что за объекты в k и v до тех пор, пока у каждого из попавших в цикле итерирования объектов можно получить хэш. И вот уже словарь перевёрнут, значения стали ключами.

Или переиспользовать одно имя переменной под разные типы объектов в рамках одного метода.

Это не только с динамической типизацией возможно:


let a = "some string";
let a = 10;
let a = Some(2.0);

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

Скорость написания скрипта, например

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


Если объект поддерживает некое производимое над ним действие — скрипт просто работает; если нет и управление заходит в ту ветку кода, в которой пытаются вызвать неподерживаемое действие — выбрасывается исключение.

Поправил.


Или переиспользовать одно имя переменной под разные типы объектов в рамках одного метода.

А это вообще ортогонально типизации.


dict((v: k) for k, v in some_entities.items()) без разницы что за объекты в k и v до тех пор, пока у каждого из попавших в цикле итерирования объектов можно получить хэш.

let flipped: HashMap<_, _> = dict.into_iter().map(|(k, v)| (v, k)).collect();


Причём в этом коде я узнаю о том, что значений не поддерживают хэширование, до запуска, а не после.

Скорость написания скрипта, например.
Вывод типов в современных языках довольно развит. Посмотрите на тот же Crystal, там почти не требуется указывать тип.
Или переиспользовать одно имя переменной под разные типы объектов в рамках одного метода.
Это уже реализовано как в Rust, так и в Crystal

А разве репл только для динамических языков есть?

Ну я плохо себе представляю пользу REPL для статических языков.
А какие есть примеры статических языков с REPL?
Как мне кажется, это не статический язык с REPL, ИМХО, это просто интерпретатор Rast, такие же есть для Си, но ни один не взлетел. Могу ошибаться.

Haskell, Scala?

Haskell, Scala, Swift.

REPL

Есть и для статически типизированных ЯП.


манки патчинг

Не вижу в этом ничего хорошего.


duck typing

А, чудесная возможность принять жёлтый снег за лимонное мороженое.

Не вижу в этом ничего хорошего.

А, чудесная возможность принять жёлтый снег за лимонное мороженое.

Ну так бы и сказали сразу что "хорошего(по версии AnthonyMikh)".
То, что лично Вы и ещё кто-то не хочет считать плюс плюсом — он не становится минусом. Он просто не нравится лично Вам и/или не подходит Вашим задачам/требованиям/руководству/феншую. Не более того.

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

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

Не совсем понял, конкретно где в питоне "разворот в сторону статической типизации" происходит. В Питоне есть "аннотация типов"(то, что обсуждается в статье и комментариях), которая подсказка для пользователей библиотек/API, для создания документации и для подсветки возможных ошибок IDE.
Ни на что большее, нежели аннотация типов, она и не претендует, хотя её и можно задействовать для квази-статической проверки.

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

Скромно пытаются и в статическую типизацию
duck typing
Сойдёт? Crystal
class A
  def initialize()
    @v = 0
  end
  
  def set(v)
    @v = v
  end
  
  def get()
    @v
  end
end

class B
  def initialize()
    @v = ""
  end
  
  def set(v)
    @v = v
  end
  
  def get()
    @v
  end
end

a = A.new()
a.set(5)
b = B.new()
b.set("5")

[ a, b ].map { |i| puts i.get() }
Это, в целом, достаточно синтетический пример, но не сложно представить чуть более развесистую логику с более сложных форматированием, для получения, например, ключа кеширования. Вариантов решения что в расте что в пайтоне больше одного, рассматривать их все, слишком объёмно. Pattern matching ждём, само собой.

Я выберу читаемость над эффективностью если это не bottle neck.

Зачем на питоне писать как на сях остается непонятным. особенно чудовищно использование дженериков. аннотации, как по мне, нужны ну разве что в крупной кодобазе на питоне. А зачем ее на питоне то писать? Чтобы побыстрее или тормозило как следует? В общем, присоединяюсь к Шарикову, «Ерунда это всё. Надо взять всё и поделить!»

Аннотации удобны — смотришь на код и сразу видно, что именно принимает/возвращает функция.

Если не хочется читать эту статью или невтерпеж ждать второй части материала, можно посмотреть видео нашего выступления.

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

пока опциональная статическая типизация в Python выглядит недостаточно мощным инструментом, позволяющим избавиться от всех ошибок, связанных с несоответствием типов

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


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

а возможно — началом конца питона.

Имхо, конец уже начался, ну или по крайней мере ухудшения того, за что так здорово питон выдвинулся. По моему мнению ошибки, если не сказать хуже это:
— 3-я версия питона с поломанной совместимостью.
— Внедрение подсказок статической типизации.
— И вообще затаскивание в ядро языка вещей типа асинхронщины.
Идем по пути превращения питона в подбие С++, ужасного монстра, на полном подмножестве которого никто толком не умеет писать.

Хмм. Готов поспорить )))


— 3-я версия питона с поломанной совместимостью.

Обратная совместимость — это зло, что можно наблюдать на примере жаваскрипта )) Но согласен, можно было получше организовать. 3.0, 3.1, 3.2 — неюзабельные версии. Что ж, на ошибках учатся. С другой стороны проблему unicode vs str без поломки совместимости было не решить, а надо было решать. Или поддержка "старых" классов...


— Внедрение подсказок статической типизации.

Крутая вещь. В больших проектах очень помогает. Главное, никто не форсирует, не нужно — не используй, нет проблем.


— И вообще затаскивание в ядро языка вещей типа асинхронщины.

Асинк — это киллер фича и то, что вернуло довольно большой маркет бэкендного веба, который заметно стал утекать на ноду. Причём асинк в питоне реализован на мой взгляд почти идеально с точки зрения пользователя. Конечно, сама концепция сложна и требует тщательного планирования и понимания нюансов, но АПИ прекрасен. И опять же, никто не форсирует.


А вцелом по развитию питона, так оно как раз, кажется, сильно замедлилось после асинка.
Последнее значительное нововведение — f-строки в 3.6.


В последнее время, с 3.7, особо ничего нового и не появилось, так по мелочи.


А насчёт ухудшений… было бы интересно услышать ваше мнение.

def last(array: List[int]) -> Optional[int]:
    if len(array) == 0:
        return None
    return array.pop()

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

Философия питона вообще-то Better ask forgiveness, than permission. Согласно этому, следовало бы использовать


def last(array: List[int]) -> Optional[int]:
    try:
        return array.pop()
    except IndexError:
        return None

К тому же, это исключение не обязательно ловить в месте, где оно возникает. Этот "минус" с точки зрения типобезопасности существенно экономит время разработки, потому что не нужно перечислять все возможные исключения только ради их перечисления. Но в теории для питона ничто не мешает принять какой-нибудь новый PEP, в котором было бы описано поведение статической проверки исключений.


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


И совершенно не обязательно заниматься этим:


давайте перепишем Python-код по аналогии с Rust, не вызывая исключения.

Лучше писать на питоне как на питоне, а не по аналогии с Rust. Ну и наоборот :)

Я думаю комментарий немного уводит в сторону изначальную мысль.
Конечно, если ты помнишь что array.pop() может бросить исключение, и еще миллион методов могут бросить исключение и везде лепить проверки, то проблем не будет. Но суть в том, что в Rust за тебя об этом помнит компилятор, ты пишешь items.pop() и он тебе говорит «Чувак, а тут вобщето не обязательно число, вектор может быть и пуст». А стиль написание, это немного ортогонально мысли из статьи.

Да, согласен. С другой стороны, я написал,


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

Если представить, что тайпхинтингу в питоне столько же лет, сколько самому Расту (который ещё сколько-то лет до релиза пилился), то в общем уже и тот результат, что имеем, неплохой. И я уверен, он будет развиваться.


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

None по мнению некоторых уже можно признавать главной ошибкой в программировании. Следующей я думаю можно признавать концепцию исключений. Исключения делают код неявным и нестабильным. Распаковка контейнера с результатом дает тебе точную проверку на наличие результата. Исключение которые пользователь библиотеки перехватывал в v1, в v2 переименовали и код неожиданно упадет с ошибкой. Вот здесь гораздо лучше расписано

Всегда будут 2 мнения. Одним солёное, другим сладкое. Код на питоне — лучший код из всех возможных императивных языков, имхо. И всё вами перечисленное без сомнения гениально. Даже если кому-то это ошибка, то по крайней мере https://www.northeastern.edu/graduate/blog/most-popular-programming-languages/ вот это говорит о том, что солидной части разработчиков она может приносить 120К баксов в год ))) а на вашем ЯП без None и исключений сколько?

None — это не null, не путайте, к главной ошибке программирования не относится. None — это нормальное значение для нормального типа.


А вот исключения, да, неприятненько.

None — это не null, не путайте, к главной ошибке программирования не относится. None — это нормальное значение для нормального типа.

У вас никогда прод не падал с ошибкой "Ой, у NoneType не такого метода"?

Бывало, но оно ничем не отличается от условного TypeError: list indices must be integers or slices, not str

Условное TypeError падает на проде или при компиляции?

None в Python — это просто переименованный null, ничем от него не отличается.


Option::None в Rust это уже совсем другое дело, и, действительно, более безопасная концепция.

Основная разница None и Null состоит в том, что None не вызывает undefined behavior. А Null вызывает. [null + 100500] куда запишет? Никто не знает.

Надо различать сишный низкоуровневый NULL и null в ЯВУ типа java/sql.


Нельзя запретить struct {int a;int b;} * x = NULL потому что железо разрешает такое (а если вам до лампочки железо, то не пишите на си). Но при этом, написать struct {int a;int b;} x = NULL (не указатель!) в си нельзя. А во многих ЯВУ можно и лично меня это шокирует.

Это в каком-таком языке (кроме питона с его "хинтами") можно иметь указанный тип и его нарушать в той же строке?

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


Object obj = null;
Не понимаю, зачем в питон тащить чуждые концепции.
Python любим как раз за низкий порог вхождения, в отличии от rust. Начните писать на питоне в манере rust, и вы получите другой язык. Который возможно найдет своих поклонников, но будет ли их так же много, сколько сейчас есть у питона? Сомневаюсь.
Пример perl6 должен заставить задуматься. Язык, в конце — то концов сделали, но успех пятерки ему даже и не сниться. Непродуманные изменения, не поддержанные сообществом, могут толкнуть питон на ту же дорожку.
Есть nim, который достаточно похож на питон, не один к одному, но все таки. Компилируемый, строго типизированный, с достаточно лаконичным синтаксисом. Сколько человек могут похвастаться его знанием?
В самом питоне есть многим, если не всем, известный пример не очень удачного заимствования — это django. Я пробовал писать на рельсах и джанго. Сравнения не в пользу последнего. Хотя отдаю должное, django появился к месту и ко времени, и не мало способствовал росту python — сообщества. Только, на мой взгляд, сейчас уже устарел и предпочтение лучше отдавать тому же flask, например.
Да и rust ни куда не денется, и питон так и останется подражателем.
зачем в питон тащить чуждые концепции.

Очень просто объясню.
Низкий порог входжения никуда не денется от того, что в ЯП есть разные фичи. Но как только тебе надо шагнуть за порог, если за ним пустота, тебе придётся менять ЯП на другой, с более высоким порогом. Питон же как раз тем и хорош, что его не надо менять как перчатки.

Вот совершенно не соглашусь. ИМХО, базовый набор фич должен быть ограничен. Для того, чтобы обеспечить легкий порог вхождения и трудность написания замудренного когда.
Дополнительные фичи, если нужны, должны вноситься в виде внешних библиотек.
Если же нужно то, что питон не может обеспечить, скажем быстродействие, нужно испольовать или внешние библиотеки, либо переписывать критические части кода на другом языке.
Насчет же статических типов и/или статических подсказок типов, то вроде как эта фишка должна помогать бороться с ошибками в сложных программах. Но, в моем личном опыте, ошибки свяазнные с типами или стоят на одном из последних мест по проблемности, или вызывают исключение, что стопроцентно укладывается в концепцию раннего падения.
Мне кажется, что если у кого-то ошибки, связанные с типами вызывает очень много трудностей, настолько, что нужно менять язык, то вероятно, что-то не так с подходом к написанию программ на питоне.
Для питона лично я бы хотел, чтобы ядро языка сохранялось бы как можно более простым и компактным.
А все писать только на питоне — это невозможно да и не нужно.
базовый набор фич должен быть ограничен

Если вы говорите о синтаксисе, то в питоне он и так довольно сильно ограничен. И расширяется крайне редко, в отличие от ЖС, в котором каждый год пол-языка добавляется. Вот гляньте в https://habr.com/ru/post/533672/ если интересно. И там мои комментарии, зачем всё это туда тащить, получили минусов больше, чем за все предыдущие 10 лет на хабре =) Каждому своё.


Дополнительные фичи, если нужны, должны вноситься в виде внешних библиотек.

В идеале — да. Но когда это невозможно, приходится синтаксис расширять. В случае с async например — @coroutine и yield / yield from очень сильно запутывали, и безусловно async/await на порядок проще. Или f-string, тоже значительно упростило многие вещи.


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

Ну опыт у всех разный. Лично мне (и со мной согласны остальные 15 человек в моей компании) оно сильно помогает работать с PyCharm, подсказки, переход к определению, подстановка атрибутов, в общем куча плюшек. Может, это даже важнее гипотетических ошибок, которые у опытных программистов и так нечасто бывают. Да и не нравится — не используй, в чём проблема, никто ж не форсирует.


А все писать только на питоне — это невозможно да и не нужно.

не всё. Но если у вас проект в 100000 строк, то чем богаче язык, тем проще писать.


Почему вы так против? В конце концов, вас кто-то заставляет учить все фичи? Вы можете использовать столько, сколько хотите. Можете даже форкнуть и удалить лишнее =)

Почему вы так против? В конце концов, вас кто-то заставляет учить все фичи? Вы можете использовать столько, сколько хотите. Можете даже форкнуть и удалить лишнее =)

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

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

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

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


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

Ага. Поэтому давайте большие и сложные программы писать на Python не будем, раз мы не хотим, чтобы он становился комфортным выбором для них. Может тогда вообще лучше отказаться от Питона и осваивать язык, который подойдет и для больших, и для малых проектов? Пусть и с не столь быстрым стартом в обучении.


Я просто хочу сказать, что ваша позиция на самом деле противоречит вектору развития языка (любого языка, претендующего на звание языка общего назначения). Конкуренция в среде ЯП пока только усиливается и у того, кто сознательно станет сдавать позиции, ограничивать возможности своего языка, шансов победить в борьбе будет сильно меньше. Но отчасти быть может вы правы, и Питону стоит подвинуться, вернуться в сферу одностаничных скриптов и пропустить более подходящие языки общего назначения на занятые им места.

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

Да, я против превращения всех ЯП в монстров типа C++. А тенденцию я вижу именно такую.
ИМХО, питон выбез наверх именно в силу своих плюсов, таких как простота ядра языка, хорошая читабельность исходных кодов написанных на нем, REPL, duck typing, жестких требовани1 к оформлению (отступы).
ИМХО статическая типизация в чилсло этих козырных фишек не входила и для меня она не важна, так как в моей практике количество ошибок связанных с типами ничтожно. А вот возможность подсунуть мок объект в любое место — неоценима.
ИМХО статическая типизация в чилсло число этих козырных фишек не входила и для меня она не важна, так как в моей практике количество ошибок связанных с типами ничтожно

У вас просто типов нормальных не было.


А вот возможность подсунуть мок объект в любое место — неоценима.

Тривиально реализуется на любом языке с интерфейсами или его аналогами.

Тривиально реализуется на любом языке с интерфейсами или его аналогами.

Вот этого как раз и хотелось бы избежать.
так как в моей практике количество ошибок связанных с типами ничтожно.
А если mypy --strict? )
Если вы говорите о синтаксисе, то в питоне он и так довольно сильно ограничен. И расширяется крайне редко, в отличие от ЖС, в котором каждый год пол-языка добавляется.

В моем представлении есть два ужасных ужаса, C++ и JS где наворочены горы ненужного.
Ну и еще в какой-то степени PHP, куда тоже тащат все, что увидят в других языках. Питон сравнительно простой и хотелось бы сохранить эту ситуацию как можно дольше.
Rust компилируемый язык. Его подход — проверил все что только можно и скомпилировал бинарь, которая, с определенными оговорками, самодостаточна.
С питоном такой финт не работает: питон компилирует модули по мере обращения к ним. Да, можно проверять модуль перед релизом, но это не то, потому что это не сработает. Стоит поменять api пакета, оставив иерархию классов прежней (это можете быть необязательно вы, за вас это может сделать один из авторов, творение которых вы помянули в депсах), и питон снова кувыркнется только при попытке вызова «поломанного» метода (Проверка typehint-а недостаточна).
Чем подход rust в обработке ошибок, например, отличается от питона, что его нужно тянуть в язык отдельно? Там на невосстановимые ошибки паникуем, на остальные возвращаем соответствующий результат, в питоне ни что не мешает делать так же, кидать эксепшн или возвращать результат, который не обязан быть простым по своему устройству.
Даже если rust подобный синтаксис подтянут в питон, его реализация должна будет нормально соседствовать с прежним подходом, учитывая размер существующей кодовой базы, смешивание 2 подходов, как минимум, не должно порождать ошибок, а то, что оно будет — это объективная реальность, а как максимум, данные изменения не должны приводить к просадке скорости существующего кода. Не факт, что это просто будет сделать.

В чем я наверное готов согласится с автором публикации — это в том, что, возможно, к инструментам проверки качества кода на питоне стоит присмотреться еще раз, в контексте идей привнесенных rust и изменений в самом python.

Дело в том, что тип Result в Rust, во-первых, является тип-суммой, работать с которым нельзя без того, чтобы явно не рассмотреть оба возможных варианта. А во-вторых, он помечен атрибутом #[must_use], что не позволяет просто забыть его обработать. И проверки этих случаев обеспечивает компилятор статически. Python не сможет такого by design.

Я пробовал писать на рельсах и джанго. Сравнения не в пользу последнего.

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


Да и rust ни куда не денется, и питон так и останется подражателем.

Это ещё кто кому подражатель, интересно. 5-летний Раст или 30-летний питон?


Непродуманные изменения, не поддержанные сообществом

это намёк на оператор моржа :=? :=) не в восторге от него, но пока что не особо и встречаю. Возможно, пройдёт пара лет, все попривыкнут.


Есть nim… Компилируемый, строго типизированный

так может в этом то и проблема?
Нужен компилируемый язык? — Жава, С, С#, тра-та-та… пара сотен таких есть, навскидку. Нужен интерпретируемый? Ну не РНР же, или ЖС с 900-страничным мануалом по синтаксису и встроенным объектам (без библиотек), где одним лишь описанием того, как работает конверсия типов, 10 страниц заняты, и где фичи можно (то есть нужно) включать флагом в файле настроек или вообще использовать внешний транслятор? Или перл, при всех его достоинствах пригодный разве что статью на хабре распарсить. Да, ещё руби, но по близости к человеческому языку и по читаемости питон всё равно намного впереди.
Нужен язык со строгой типизацией времени выполнения? GOTO предыдущий пункт. Не нужна такая типизация? Опять же, см. выше.


Так что пока не вижу причин беспокоиться за судьбу питона.

Во-первых в Rust'е можно иметь динамические типы (dyn).


Во-вторых, типизация питона ужасающа. Типы в кавычках, отсутствие Self (хотя, казалось бы, что мешало завести вместе с остальными типами?)


class X:
  @classmethod
  def from_int(cls: 'X', i: int) -> 'X'
    ....

Если вам в этом месте не захотелось плюнуть на типизацию в Питоне, то вы мало писали на Rust.


Вообще, если сравнивать питон с Rust'ом, надо начинать с вот таких вот штук:


>>> a=([],)
>>> a[0].append(0)

А вот место, в котором надо ругать Rust — это за отсутствие yield. Нет ни одной причины, почему нельзя было не завести yield в язык. Но нет.

Выглядит так, как будто генераторы в Rust есть, даже с тем самым ключевым словом yield.

UPD: А, это ещё RFC. Только в процессе обсуждения

Очень, очень интересно. Спасибо, буду ожидать с большим интересом.

По поводу типов в кавычках: нужно добавить в модуль
from __future__ import annotations

Да, придется дописывать лишнюю строчку (или прописать её добавление в isort через add-import), но тогда кавычки использовать не нужно.

Но Self (тип self) это не привезёт, увы.

А вот место, в котором надо ругать Rust — это за отсутствие yield. Нет ни одной причины, почему нельзя было не завести yield в язык. Но нет.

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

Расшифруйте. Отсылки вида "на-раст-смотрите" говорят примерно ни о чём.

Система типизации в языке не та вещь которую можно рассматривать по кусочкам. Оно только в комплексе языка работает. Надо несколько языков попробовать чтобы сравнивать. Вот например в тоже TypeScript очень хорошо сделана типизация. У разных языков есть концепции которые реализованы и работают лучше чем в других языках. Вот например про self в rust. Вот два куска кода которые делают одно и тоже в python и в rust
from __future__ import annotations
from dataclasses import dataclass

@dataclass
class User:
    name: str

    def change_name(self, new_name: str) -> None:
        self.name = new_name

    @classmethod
    def new(cls, name: str) -> User:
        return cls(name=name)

user = User.new("Тест")
user.change_name("Тест2")

struct User {
    name: String
}
impl User {
    fn change_name(&mut self, new_name: String) {
        self.name = new_name;
    }
    fn new(name: String) -> Self {
        Self{name}
    }
}
fn main() {
    let mut user = User::new("Тест".to_string());
    user.change_name("Тест2".to_string());
}

В rust сразу видно три интересные концепции.
1) ключевое слово Self для указания как на тип так и на саму структуру для которой реализуется метод. В python тоже известно что за тип будет подставляться в cls но использовать его для типизации нельзя.
2) Все аргументы в структуру подставляются по именам. но если имя переменной и имя поля структуры совпадают то можно указывать в упрощенном варианте
3) В rust нет разделения на staticmethod и на classmethod. А различие между method и classmethod компилятор сам понимает в зависимости от того передается в функцию self или нет. И доступ до метода будет разный через "." или через "::"

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


Кроме того, истовое желание писать на питоне как на расте(или наоборот) зло куда большее, нежели ненравящаяся типизация.

Я не могу просто взять и пройти мимо данного комментария.


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


class User:

    def __init__(self, init_name):
        self.name = init_name

    def change_name(self, new_name):
        self.name = new_name

user = User("Тест")
user.change_name("Тест2")

Вам действительно неизвестно, какой инстанс класса вы создадите, вызывая User? Или Вам неизвестно, как работает конструктор класса в python?


К самой статье у меня тоже много вопросов, но все они решаются данной строкой:


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

Что такое можно писать, и каким образом, чтобы потребовалась типизация на питоне, я реквестирую примеры.


Неужели кто-то достаёт из чужих продуктов обфусцированный код и пытается его пихать в свой продакшен, и ему сложно понять, что же такое var2314 = randomCls2149(a1, a2, a3) ?

Зато совершенно непонятно, какого типа должен быть name. Сегодня вроде бы вполне нормально работает строка, а через некоторое время уже на продакшене выясняется, что автор подразумевал какую-нибудь специальную структуру UserName и забыл отметить это в документации, и в итоге всё падает с ошибкой AttributeError: 'str' object has no attribute 'first_name', ведь в коде этого случайно никто не заметил и автоматические тесты случайно не покрыли этот кейс.

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


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

Ну вот, как можно видеть прямо в этом посте, как минимум у Рамблера подобные штуки успешно доезжают до продакшена

Зачем документировать то, что можно указать типами? Документация — это информация для человека и она может не соответствовать реальным данным в коде, потому что происходит дублирование. Типы же понятны не только человеку, но и машине. И машина может проверить автоматически их соответствие. Там, где вводятся типы, специально документировать их наличие не нужно вовсе.

Я именно это же самое и имел в виду в своём ответе, что тип инстанса класса User будет очевидным. И вообще питон настолько очевидный и простой язык, что если где-то написано сложно или грязно, значит разработчик, скажем мягко, не старался.


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


И я буду первым, кто навсегда забросит питон, как только в нём (в чём я сильно сомневаюсь) будет требоваться явная типизация.

Ну так для очевидных вещей писать аннотации типов никто и не заставляет. Вот такой код содержит все нужные аннотации типов и успешно пройдёт проверку mypy --strict:


class User:
    pass

user = User()

Некоторые менее очевидные, но достаточно простые ситуации mypy способен додумать самостоятельно, например вот здесь никакие аннотации для списка не требуются, всё и так понятно по первому вызову append и mypy --strict тоже успешно проходится:


class User:
    pass

users = []

users.append(User())

А вот name из вашего примера выше — совершенно не очевиден и требует пояснений. И между документацией и аннотациями типов я выберу аннотации, потому что они, в отличие от документации, проверяются автоматическими инструментами и, как следствие, не могут врать. А вот с врущей документацией я сталкиваюсь регулярно.

и, как следствие, не могут врать.

"Don't be so sure, Mr President!"©
Наверняка можно создать код, который поставит в поле не то, что увидит ожидаемым там любая проверка линтером.
И при этом не уронит код.

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

Blender с версии 2.80 и далее стал требовать от аддонов явно задавать поля для ряда структур, которые потом в сохранённый файл пойдут.
Но они используют свою сборку питона.

Расшифровываю: В методе класса нельзя написать сигнатуру, которая переживёт наследование.


class X:
  def combine (self, other: 'X') -> 'X':
    return other

Что станет с сигнатурой combine при наследовании X?


class Y(X):
   pass

Какой возвращаемый тип у Y().combine()?

TypeError: combine() missing 1 required positional argument: 'other' :)


Но если не занудствовать, то можно эмулировать Self костылями


from typing import TypeVar

Self = TypeVar("Self")

class X:
  def combine (self: Self, other: Self) -> Self:
    return other

class Y(X):
   pass

Y().combine(Y())  # Revealed type is 'Y*'

Ох, всё ещё хуже. Я написал что-то странное. Я ожидал вернуть инстанс класса, а получился монстр, которого я просто разобрать не могу. Попробуйте сами поиграться с вашим кодом… Почему combine возвращает класс, а не инстанс?


В расте всё так просто было… self — инстанс, Self — его тип. А тут какая-то интроспективная жуть.


type(Y().combine(Y()))
<class '__main__.Y'>

Вы всё напутали. combine() возвращает инстанс, а вот type() возвращает уже класс этого инстанса.

Собственно, аннотации тут вообще ни при чём, без них ведь то же самое будет


class X:
  def combine (self, other):
    return other

class Y(X):
   pass

print(type(Y().combine(Y())))  # <class '__main__.Y'>
Какой возвращаемый тип у Y().combine()?

Тип TypeError. Как и при X().combine()


И, поскольку эта типизация подсказка программисту и среде, в среде будет подсветка ошибки для любого переданного не-экземпляра класса(X или Y) или наследник от оных. Если среда поддерживает, конечно. В PyCharm будет.


А так — во всех случаях "то, что туда передаст программист". Эта типизация совсем не мешает передать что-то совсем другое, даже не унаследованное от ожидаемого типа… и получить валидный результат, если передающий программист озаботился совместимостью данных/протоколов с тем, что ожидал программист метода.


В плюсах именно что возможность передать что-то другого типа, но совместимое по протоколам/форматам, не борясь с правильным указанием "ожидаем такой вот тип объекта, и такой, и такой, и вот на такой надо перегрузку тоже указать" для компилятора.
В минусах — исключения времени исполнения и отсутствие гарантии что придёт именно ожидаемый тип объекта.
Кому что больше перевешивает.

Код на Crystal
class A
  def a
    puts "a"
    return self
  end
end
 
class B < A
  def b
    puts "b"
  end
end
 
B.new().a.b

Да есть там причина. yield создаёт самоссылающуюся структуру данных, со всеми проблемами await. А await и появился-то совсем недавно...

Данная статья будет неполной без упоминания, что в Питоне тоже есть `Result`, `Maybe` и тд.
И типизация замечательно работает: github.com/dry-python/returns
Там про это есть отдельный абзац, ссылка, к сожалению, потерялась по дороге
Никита, поправил. Не было ссылки, как Макс уже написал.

dyn Trait не обязательно боксить:


fn validate(documents: Vec<&dyn Review>) -> Vec<&dyn Review> {
    documents
        .into_iter()
        .filter(|document| document.approved())
        .collect()
}

fn main() {
    let documents: Vec<&dyn Review> = vec![
        &Article,
        &PhotoGallery,
        &Test,
    ];
    let approved = validate(documents);
    assert_eq!(approved.len(), 3);
}
Да, вы правы, спасибо!
Коллеги, у меня немного не технический вопрос.
В статье мы расскажем о том, что заставило нас отойти от привычного стека технологий

Вот скажите, вы такие сидели писали на питоне много лет, много-много кода.
Вам говорят, сделайте новый проект. И вы «мы его будем писать на Раст, ничего что у нас нет в этом опыта, да и Раст мы первый раз видим, но этот новый и важный проект для бизнеса мы напишем только на Раст», и вам отвечают «ОК! Вот вам сундук с золотом»

Не стеб, правда интересно, как это происходит.

И еще, вот если ваша команда уволится, кто будет поддерживать одновременно проекты на питон и раст? Неужели таких спецов на рынке вагон?
Вопрос вполне понятный. Давайте расскажу. Сначала тимлид нашего проекта cbmw сделал небольшой прототип для того, чтобы понять насколько готова инфраструктура – есть ли для Rust веб-фреймворки, орм, чем собирать метрики, логи и проч. Сделали нагрузочное тестирование. Потом подключилась часть команды (python-разработчики) и написали несколько дополнительных ручек апишки. В итоге стало понятно с чем будем работать, какие плюсы принесет и насколько сложно python-разработчику начать писать на Rust. Взвесили известные проблемы с ошибками, производительность Python-приложений и риски, которые Rust принес. Поняли, что оно того стоит :)
насколько сложно python-разработчику начать писать на Rust

Вот здесь по-подробнее пожалуйста
Я смогу ответить на ваш вопрос не в общем случае, а только применительно к нашей команде. Если выделить суть – когда в команде есть кто-то, кто уже пишет на Rust и может помочь разобраться с ошибками, то можно почитать Rust Book и сразу начать писать код приложения. Поначалу придется много дергать того, кто уже понимает, как разобраться с ошибками, через месяц-два поменьше :) Еще через полгода общая картина наконец начнет складываться и тогда можно помогать другим разбираться с ошибками.
if len(array) == 0:

А вы точно python программист? Python программист напишет тут


if not array:

Я люблю Rust но вы сравниваете тёплое с мягким. Если вы хотите находить ошибки локально вам не mypy писать надо а тесты.

… И теперь код работает в том числе и с целыми числами, и с булевыми значениями там, где работал только с объектами с методом __len__. Хороший размен, да.

Мне вот интересно, а что проще для изучения? C или Rust? А C++ vs Rust? Было бы хорошо изучить быстрый язык уровня С. Но отпугивает его монструозность, постоянное траханье с памятью, соответствующие баги, сложность изучения и использования. Rust сильно облегчает работу с памятью, не даёт сделать бОльшую часть багов, и вроде бы сильно меньше склонен усложнять жизнь, чем C\C++. Глянул я парочку crash cours-ов по Rust-у. Даже после питона, выглядит на удивление не сложно и дружелюбно.

Вот мне и стало очень интересно. Для новичка в системных языках, C\С++ сложнее в изучении и использовании чем Rust?

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