Comments 305
Неуверенность в том, что будет дальше. Оно и очевидно, если раньше он играл ключевую роль в принятии решений, сейчас такого человека нет. И появится ли он — неизвестно. И каким либо хаотическим (в хорошем смысле слова) образом мы получим одну из обозначенных выше формаций
Эта фейковая мультитредность, выгодная только когда узкое место диск или сеть, убивает питон :( Особенно в эпоху, когда в процессорах всё больше ядер :(
Я понимаю, что для неопытных разработчиков нужен GIL, так как они не осилят справится с рейсингом, но почему бы не оставить GIL по-умолчанию, не добавив так же что-то вроде true_multithread модуля, а так же его поддержку во все модули стандартной библиотеки?Потому что это бессмысленно. Сделать такое можно, это даже несколько раз проделывалось (скажем разработчики GLibC упорно тянут эту тему), но оказалось что писать библиотеки, которые эффективно использовать и в однопоточных системах и в многопоточных — ещё сложнее, чем те, которые «знают» и используют многопоточность.
Потому лучше уж два языка: один — принципиально многопоточный, другой — принципиально однопоточный.
Невозможно создать универсальный инструмент, подходящий для абсолютно всех ситуаций. Нельзя болгаркой и ремонтировать авто, и строить дома.
Если вы интересуетесь авто-мото темой, то наверняка заметили, что львиная доля трепотни об авто — это обсуждения наподобие: «Как жаль, что нет авто, с двигателем, который проезжает 1,000,000 км без капиталки, который отлично управляется на треке, в городе, и проезжает в болоте, да еще, чтобы стоил дешевле $20,000 и налоги на него были невысокие». Или в мото: «Хочу турэндуро с движком 1,5 л и 200 лошадей, чтобы и на треке гонять, и в выходные по лесу. И с запасом хода от 500 км. При этом быть легче 150 кг, легко рулиться, и быть невысоким».
Начиная с какого-то момента требования становятся противоречивыми. Можно увеличить мощность, но это увеличит вес и стоимость и снизит надежность. Можно снизить вес, но для этого придется использовать менее мощный мотор и более дорогие материалы (легкие сплавы вместо стали).
Выхода два: использовать несколько инструментов, каждый для своей задачи, или мириться с недостатками, при использовании не по назначению.
Если бы существовал идеальный инструмент для всех задач, то все бы на него перешли, и language holywars ушли бы в прошлое.
- 1-поточные, 1-процессные
- многопоточные и многопроцессные
- распределённые вычисления
Наблюдение Мартелли заключается в том, что первая категория программ может решать всё более сложные задачи в связи с ростом скорости одного ядра CPU. Поэтому вторая категория становится всё менее актуальной (за исключением игр, а я бы добавил сюда VR и всякие мультимедийные приложения типа обработки аудио, монтажа видео и пр.). К тому же основная проблема со второй категорией в том, что она плохо масштабируется — если объём требуемых вычислений возрастает немного, то одного процессора уже не хватает (потому что уже используются все ядра) и задача перетекает в другой класс. Поэтому, по мнению Мартелли, иногда лучше сразу писать для распределённых вычислений (Amazon и пр.).
Во-первых рассуждения Мартелли мне кажутся устаревшими (второе издание Python Cookbook, в котором автором был Мартелли, вышло в 2005 году) — сейчас мы видим значительное снижение скорости роста производительности одного ядра, а количество ядер напротив разрослось уже до 20+ — и это не серверные процессоры.
Во-вторых эту логику (правильную — следует оценивать экстремумы) можно обратить против Python — он настолько неприлично медленный (я сейчас даже не про GIL и concurrency, а вообще), что его очень быстро начинает не хватать даже для задач 1-й категории. Python плохо масштабируется. Поэтому используя его мы рискуем гораздо больше, чем в случае остальных языков — задача может перетечь в другой класс гораздо раньше.
Честно говоря, я пока еще не встречался с задачами, где затык в производительности был бы именно в Python, поэтому ничего о производительности интерпретатора не знаю.
Но ведь идет же постоянная работа над новыми версиями, и Python используется в больших корпорациях, наверняка, у них есть ресурсы, чтобы оптимизировать интерпретатор до уровня того же Perl. Это вряд ли невыполнимая задача, может быть даже проще решаемая, чем для Perl и JS.
Но раз этого не делают, значит никому оно и не надо?
Разумеется при таком подходе оптимизировать интерпретатор до уровня того же Perl — особо не нужно…
Я не увидел по ссылке «тормозит и глючит», а увидел: «Ван Россум не захотел добавлять те плюшки, которые нужны нам, вот мы и форкнули».
Да и вообще, это какой-то слишком уж упрощенный взгляд.
И скорость еще не главное. Вот есть очень быстрый, довольно популярный (для своего времени, во всяком случае) проект на Perl под названием awstats. По бенчмаркам он затыкает по скорости практически всех конкурентов. Но код там такой… Волосы на голове шевелятся и стремятся уйти.
Я не увидел по ссылке «тормозит и глючит», а увидел: «Ван Россум не захотел добавлять те плюшки, которые нужны нам, вот мы и форкнули».А ничего что после Ван Россума, написавшевго Mondrian/Rietveld были ещё Gitosis/Gitolite и первая версия Gerrit'а? Всё это — на основе всё того же кода?
А вот уже переписанная версия — даже отдельного названия не получила, только номер?
В том-то и дело, что переписываения произошло внутни развития одного проекта, а не как переход от одного проекта к другому.
Странно, что вы там этого не увидели…
Но код там такой… Волосы на голове шевелятся и стремятся уйти.Ну вот и с питоном — та же фигня. Просто начиная с какого-то момента код с динамической типизацией становится неподдерживаемым.
Это может быть из-за большого количества данных (== «тормозит» из моего описания выше), но у крупных компаний, зачастую — из-за размеров кодобазы (== «глючит» оттуда же).
PEP 484 оттуда родился — но пока мало статистики, на которой можно понять — реально решает он проблему или нет…
Просто начиная с какого-то момента код с динамической типизацией становится неподдерживаемым.Это, в первую очередь, зависит от кривизны рук, а не от вида типизации.
с какого-то моментаС какого момента? После n-лет разработки или после n-Мб кода? Если это происходит когда python код переваливает за 10Мб, то можно не волноваться — для 99,9% проектов этого лимита хватит.
Возможно. Но факты — вещь упрямая. Если над проектом работают несколько сотен программистов, то код, на динамическом языке без типизации «рассыпается». Проверно многократно.Просто начиная с какого-то момента код с динамической типизацией становится неподдерживаемым.Это, в первую очередь, зависит от кривизны рук, а не от вида типизации.
С какого момента? После n-лет разработки или после n-Мб кода?После того, как на проекте разработчики сменятся 2-3 раза. Или просто когда их станет больше 100. Потому что между тем местом, где порождён неправильный тип и где он вызвал выброс исключение оказывается слишком много компонент. А без разбиения продукта на компонетны вы вообще ничего не сотворите в команде в пару тысяч человек.
Если это происходит когда python код переваливает за 10Мб, то можно не волноваться — для 99,9% проектов этого лимита хватит.В «мелких» компаниях — может быть. Но речь шла о крупных. О том почему они не могут «допилить» python хотя бы до уровня perl'а.
Ответ: потому что им это не нужно по вышеописанным причинам. А у мелких компаний свободных ресурсов нету.
После того, как на проекте разработчики сменятся 2-3 раза. Или просто когда их станет больше 100Ну вон например в джанго почти 900 контрибьюторов, и ничего не развалилось.
И да, они прикладывают очень много усилий для того, чтобы «не развалилось»: огромное количество документации, тестов и прочего для такого, относительно небольшого, проекта.
И да, они прикладывают очень много усилий для того, чтобы «не развалилось»: огромное количество документации, тестов и прочего для такого, относительно небольшого, проекта.
Вы так говорите, как будто это что-то плохое
Но если выясняется, что для того, чтобы всё это работало, вам требуется потратить ещё вдвое-втрое больше времени на написание документации и тестов, то это преимущесво нивелируется, а недостатки (на два порядка меньшая скорость работы, невозможность использования многоядерных процессоров) — остаются.
Но если выясняется, что для того, чтобы всё это работало, вам требуется потратить ещё вдвое-втрое больше времени на написание документации и тестовДля проектов со статической типизацией тесты и документация важны не меньше, тут вы не выиграете, кроме того оно не влиет на то работает приложение или нет.
У вас ложные домыслы, вы хаете чужое «болото» не видя картины в целом, пообщайтесь с профисиаоналами которые разратывают одновременно на питоне и с++ (java).
невозможность использования многоядерных процессоровОпять домыслы, в питоне нет проблем загрузить все ядра процессора.
на два порядка меньшая скорость работыУ меня был реальный случай где было написано 2 варианта одного компонента на python и golang, дак вот python вариант был в 3 раза быстрее за счет того что там использовалась маленькая с++ вставка. Вы можете сказать «дак это из-за с++», дак вот так и надо разрабывать, кроме того много библиотек под питон как раз написаны на с/с++.
PS: На счет «меньшего» кол-ва ошибок со статической типизацией есть смешной случай от Гвидо.
Для проектов со статической типизацией тесты и документация важны не меньшеВам не нужно ни описывать, ни тестировать параметры большинства функций в статически типизированном языке. Само описание является и документацией и тестом, причём, что важно — обойти их, без специальных ухищрений нельзя.
А насчёт «профессионалов, пишущих на python и на других языках» — ну я, например, сам таким являюсь. С этого всё и началось: с наблюдения, что большие проекты, изначально написанные на python'е, рано или поздно оказываются переписанные на Java (чаще), либо C++ (реже).
Когда этот вопрос подняли, то в ходе дискуссии был упомянут как раз Геррит — типа «если проект написан самим Гвидо, то он, возможно, сможет жить и на питоне»… после чего кто-то прислал ссылку на как раз разрабатываемую вторую версию — после чего вопрос был закрыт.
Мелкие утилиты, кстати, и у нас часто на python пишут до сих пор.
Вам не нужно ни описывать, ни тестировать параметры большинства функцийКак и в питоне (зависит от проекта, исключение — публичные библиотеки, хотя для них делают доку и для «статических тип. языков» тоже), пример — исходники djangocms, параметры почти нигде не описаны, входные параметры так же почти нигде не проверяются — и это не проблема.
А насчёт «профессионалов, пишущих на python и на других языках» — ну я, например, сам таким являюсь.Ок, видимо сделал неверный вывод по тому как у вас какие-то сложности использовать питон на всех ядрах процессора.
большие проекты, изначально написанные на python'е, рано или поздно оказываются переписанные на Java (чаще), либо C++ (реже).Это вовсе не означает, что старт на питоне был ошибкой. Тут часто упоминают теорию — что если бы они изначально начали с java/c++ то стартап не взлетел бы вообще. А мотивация переписывания может быть вызвана другими причинами.
Кстати, я сейчас подумал — что развивать проект с исходниками в 10Мб по сложности должно быть сопоставимо с проектом в 100Мб+, т.к. архитектура уже выстроена и проект просто прирастает новыми компонентами/микросервисами. С такими объемами одному человеку не возможно знать весь прокт досконально. А если так, то у питона нет особых проблем с большими проктами при правильной архитектуре.
Лучше бы вместо нового оператора присваивания сделали настоящие треды.
Пробовали уже, например здесь или здесь. Почему эти реализации широко известны только в узких кругах, это другой вопрос.
GIL, скорее всего, был добавлен для упрощения реализации, в то время, когда поддержка потоков была не особо актуальна, например, потому, что был perl, который справлялся и в один поток. Правда это мое мнение, с которым вы можете не согласится. :)
приводит к падению популярности Питона
Не согласен. Пых, которому предсказывают закат последние лет 10, и питон — одни из самых востребованных языков. Я как раз ищу работу, и отслеживаю вакансии.
Одна программка, опрашивающая сетевое оборудование, на питоне цикл опроса занимал полторы минуты, на гоу без изменения алгоритма стало 4 секунды. Как-то так.
Есть более скоростные реализации, чем CPython,
но они не так распространены.
Как вы собрались к языку, в котором есть eval компилятор прикручивать?
Это редко используемая команда. А в случаях, когда она всё же используется, вполне можно из скопилированной программы дёргать libpython.
Да, eval — это команда, интерпретирующая строку на Python. Но это не значит, что программа, которая её содержит, не может быть скомпилирована.
Можно сказать, например, что библиотека V8 добавляет в C++ команду eval, выполняющую код на js. Это же не делает компиляцию C++ невозможной.
Скажем, компилятор Nuitka, который я упомянул ниже, полностью совместим с Python 3.6.
Одна программка, опрашивающая сетевое оборудование, на питоне цикл опроса занимал полторы минуты, на гоу без изменения алгоритма стало 4 секунды. Как-то так.Возможно, если кол-во сетевых оборудований исчисляется сотнями тысяч, но скорее всего программа просто криво написана либо помимо io программа делает кучу вычислений. Питон может обрабатывать до 100krps (и более) в один поток — для опроса оборудования этого достаточно.
А SNMP, ну, не знаю, как-то так:
from pysnmp.entity.rfc3413.oneliner import cmdgen
cmdGen = cmdgen.CommandGenerator()
errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
cmdgen.CommunityData(community),
cmdgen.UdpTransportTarget((host, 161)),
oid_in_octets)
даже после перехода с перла на питон удивило, что SNMP стало работать заметно медленнее, не критично, но всё равно.
ssh/snmp можно использовать под asyncio — отправляете команду сразу на 1000 девайсов, через пару* секунд у вас результат с 1000 девайсов.
Если девайсов не так много то можно завернуть готовый синхронный код в потоки (они в питоне настоящие, хотя и с нюансами).
силу медленности самого SSHВ том и дело, что это по большей части зависит от сервера и сети, без разницы на чем клиент написан.
pysnmp — библиотека не из stdlib. Вполне возможно, что она просто неэффективно написана.
Если то, что про скорость Питона написал один из комментаторов правда, то не важно есть там треды или нет — в этом случае ограничителем является скорость, а не наличие/отсутствие тредов.
Скорость синглтреда у CPU почти не растёт, тогда как количество ядер растёт. Есть граница, когда более медленный питон приемлим, и когда уже нет. То, что он не поддерживает легковесные треды, а только процессы (со своими ограничениями...), год за годом увеличивает такой разрыв.
Я бы очень хотела появления в питоне хоть и более медленных тредов, чем в Go, но всё же настоящих, а не таких, как сейчас, которые работают только медленнее на любом CPU-интенсив коде. Хочется, чтобы масштабирование было не линейным только из-за закона Амдала, а не по тому, что GIL
@numba.jit(nopython=True, parallel=True)
def logistic_regression(Y, X, w, iterations):
for i in range(iterations):
w -= np.dot(((1.0 / (1.0 + np.exp(-Y * np.dot(X, w))) - 1.0) * Y), X)
return w
(см. numba.pydata.org/numba-doc/0.39.0/user/parallel.html)
Поменять реализацию подсистемы исполнения, чтобы однопоточная реализация питона не потеряла в скорости, при этом появилась возможности исполнять многопточный код — сама по себе задача нетрвиальная. Кроме этого, сто процентов, придется подвергнуть ревизии весь существующий код на питон, потому что старая реализация не подразумевает многопоточность, со всеми вытекающими из этого факта последствиями.
Скорее всего, это и служит препятствием для распространения альтернативных реализаций питон, где с паралельностью ситуация обстоит лучше.
ЗЫ. Я практикую питон начиная года с 2005, и потребность в потоках у меня возникала, от силы, пару раз. Я думаю что прблема отсутствия полноценного массштабирования по ядрам процессора, или по процессорам в случае многопроцессорной системы, имеет место быть, но ее роль сильно преувеличена.
и питон максимально близок к, например, Go, в том, что относится к оптимизации исполнения «паралельного» кода, хотя и уступает ему.
Разве что только в случае I/O и network интенсив кода :(
я практикую питон начиная года с 2005, и потребность в потоках у меня возникала, от силы, пару раз.
Я девопс, часто рисую демоны (т.к. поддерживаю инфраструктуру), парсилки терабайтных асинхронных потоков, разные брокеры, и мне нужно постоянно :(
ИМХО PEP 572 совсем не python-style.
(holywar_started := True)
А есть где-то определение python-style?
printf("%d",(a++)+(a--)+(++a)+(--a));
Коротко и лаконично, да.
А по поводу нового оператора — я что-то не понял из PEP, как будет решаться такой случай:
if True or (something := a + 456):
print(something)
Что будет в результате?
Т.е. такой код:
diff = a - b
if diff:
doSomething(diff)
мы можем заменить таким:
if (diff := a - b):
doSomething(diff)
Не могу сказать, что я хотя бы раз сталкивался с ситуацией, в которой жалел о том, что такой фичи нет в Питоне.
Именно поэтому данный PEP вызвал такую эмоциональную реакцию у сообщества, все считали что это не баг, а фича.
Вообще, насчёт «сразу сделать» в ЧаВо Python’а есть ответ:
Почему я не могу использовать присваивание в выражении?
Многие люди, привыкшие к C или Perl, жалуются, что хотят использовать эту идиому C:
while (line = readline(f)) { // что-то делаем с line }
в то время, как в Python’е вы вынуждены писать так:
while True: line = f.readline() if not line: break ... # что-то делаем с line
Причина, по которой в выражениях Python’а не допускаются присваивания — распространённая трудноуловимая ошибка в других языках, вызванная следующей конструкцией:
if (x = 0) { // обработка ошибки } else { // код, работающий только при ненулевом x }
Ошибка заключается в простой опечатке:x = 0
, в результате чего переменнойx
присваивается значение 0, хотя, конечно же, подразумевалось сравнениеx == 0
.
Было предложено много альтернатив. Большинство из них было хаками, которые давали некоторую экономию в наборе кода, но использовали странный или непонятный синтаксис или ключевые слова и не отвечали простейшему критерию для предложений об изменении языка: оно должно предлагать человеку, ещё не знакомому с конструкцией, интуитивно правильный смысл.
Интересным наблюдением является то, что большинство опытных программистов на Python’е признают идиомуwhile True
и, похоже, не сильно огорчаются отсутствию присваивания в выражениях; сильное желание добавить его к языку выражают только новички.
Существует альтернативный способ написания такого цикла, который кажется привлекательным, но он, как правило, менее надёжен, чем решение сwhile True
:
line = f.readline() while line: ... # что-то делаем с line line = f.readline()
Проблема заключается в том, что если вы задумаете изменить способ получения следующей строки (например, захотите изменить его наsys.stdin.readline()
), вы должны будете помнить о том, что в вашей программе необходимо изменить два места — второе вхождение спрятано внизу цикла.
Наилучший подходом будет использование итераторов, позволяющих перебирать объекты инструкциейfor
. К примеру, файловые объекты поддерживают протокол итератора, поэтому вы можете просто написать:
for line in f: ... # что-то делаем с line...
В этом вся и проблема — Python 20 лет существовал без этого и тут внезапно, в году 2018 от рождества христова, эта фича резко понадобилась. Кому? Какому-то секретному клану, который 20 лет тихо ненавидел язык за отсутствие этой фичи, но упорно использовал язык дальше, вместо того, чтобы переключиться на один из стопитцати других языков?
Эту фичу можно было добавить в язык 20, 15, 10 лет назад. Очень легко, Python сам написан на C, где присваивание является operator, а не statement, так что не подумать о такой возможно было невозможно. Подумали, и *специально* не добавили. Это было отражено в FAQ'ах и так далее. Внезапно добавлять ее через 20 лет эволюции языка — мягко говоря странно. Вообще, ситуация в Python в последних релизах можно отразить одной фразой — «The king is mad». От этого устали и окружающие и, как оказалось, сам король тоже.
> Так все популярные языки развиваются.
Это типичная ошибка считать любые изменения «развитием». Часть изменений является «упадком», «гонкой за собственным (или, что хуже, чужим) хвостом» и «метастазами».
Вот например, только сегодня попалась ссылка по C++: gist.github.com/bkaradzic/2e39896bc7d8c34e042b
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
process(chunk)
# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]
# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
С одной стороны, оно конечно удобно. С другой стороны, с моей дилетантской точки зрения кажется странным сначала избежать возможности ошибок, наподобие «if (x = y)» вместо «if (x == y)», а потом вернуть её.
Я в руби на этом обжигаюсь время от времени (после многих лет паскаля).
Я программировал «одновременно» на Delphi, PL/SQL, Perl, C++, Python и JS (такой зоопарк развёлся, в том числе и моим усилиями, в одной конторе)… так вот в этом зоопарке, я часто втыкал специфичные операторы куда нужно и куда нельзя в других языках.
Правда я использовал хитрый хак от кого-то из крутых (кажется от Стивенсона), писал в операторах сравнения константу первым операндом. Но честно говоря, после отпуска самому приходилось привыкать к такой нотации.
if (500 == error_code) {
Как оно сейчас глаза режет тем кто поддерживает мой код,… хе-хе…
Поддерживаю вашу позицию на счет ":=" и "==".
Лично мне часто не хватает вот такой возможности:
Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
if (isSomenting = something1 === something2) {
Как способ выстрела в ногу, это конечно не самый топ, но разбираться в такой логике не очень приятно.
А ведь можно ещё так
filtered_data = [ z.group(1) if z := re.match(y) else None for x in data if (y := f(x)) is not None]
А можно ещё и не так. И можно вспоминать старые холивары Python vs Perl, умершие казалось бы с перлом. Поскольку теперь Perl будет казаться отлично структурированным и прекрасно читаемым языком)
И проблема поиска сильных перлистов была ещё 10 лет назад — не потому что рынок выметен, а потому что спецов стало меньше.
Как ни странно 5 лет назад мне предлагали работу на перл, но с зарплатой перловому сеньору, процентов на 40 ниже фронтового миддла) Типа поддержка — это не разработка, так зачем платить много )
А вот перл… Хотя в плане обучения перл очень хорош как язык третьего-четвёртого курса. Когда надо учить не просто программировать, а программировать читаемо.
Надо на эту тему статью тиснуть, похоливарить, жаль первое апреля ещё не скоро))))
А на перле достаточно просто программировать читаемо. Вопрос, что если сравнить языковые конструкции перла и питона, совершающие одинаковую работу, то на перле выходит не так уж чтобы интуитивно понятно, нужно знать синтаксис. А питон скорее всего поймёт тот, кто питон первый раз видит. Потому люди в питон идут. Те кто не профессионалы, не гуру, а им просто надо решать какие-то задачи небольшого объёма своими силами. Тем более под питон написаны очень крутые библиотеки. И на многие вопросы можно за пару минут найти ответ на стековерфлоу.
На фри паскаль/лазарус сконвертировать не удалось.Не расскажете в двух словах, почему не удалось? Я как-то сталкивался с парой проектов на лазарусе, весьма приятные впечатления остались. А уж скорость компиляции паскаля, особенно после C++, просто сносит башню, конечно (в девяностые это было не так заметно).
А по скорости компиляции сейчас Golang очень хорош. Вплоть до того, что можно через go run просто запускать как в скриптовом режиме.
Не расскажете в двух словах, почему не удалось?Потому что Delphi — это не только Delphi. Во все проектах, которые видел, половина всего — это куча компонент с непонятными лицензиями и непонятно откуда взявшимися. Под Lazarus всё это придётся переписывать. А если половину придётся переписывать всё равно — то лучше взять более mailstream продукт и переписать всё.
С дельфи немного другая ситуация была во времена расцвета. Не было репозитариев, не было гитхаба. Поэтому действительно можно сказать — непонятно откуда взявшиеся компоненты. Хотя были сайты с большими подборками, но это было народное творчество.
Про тот же питон.Нет. Про python и perl какой-нибудь так сказать нельзя. Ибо там очень мало комменрческих компонент всегда было.
И даже в каком-нибудь C++ не было принято тащить библиотеки неясного происходжения к себе в проект.
Не было репозитариев, не было гитхаба.Но был CTAN и был CPAN.
Погубило Delphi другое. То, что модули можно было распространять в виде бинарников. С PHP, Perl'ом, Python'ом такое не прокатывало в силу интерпретируемости. С С/C++ — потому что была куча компиляторов и у них были несовместимые бинарники. Плюс, опять-таки, GNU/Linux — туда без чёткой лицензии биболиотеки не допускались, спасиба Столлману.
А вот Delphi и Visual Basic — таки да. Причём поскольку Visual Basic был распространён на Западе, где компоненты всё-таки многие покупали, то понять — откуда что взялось всё-таки удавалось.
Хотя были сайты с большими подборками, но это было народное творчество.Проблема в том, что всё это «народное творчество» интересовалось соблюдейнием авторского права чуть менее, чем «никак». Соотвественно оно только усугубляло проблему: если нужно было что-то такое «навороитить», то оно помогало, конечно, но если потом, вдруг, возникала задача всё легализовать… Было проще всё выкинуть и заново написать, чем концы найти и связаться с разработчиками компонентов!
Погубило Delphi другое. То, что модули можно было распространять в виде бинарников. [...] Проблема в том, что всё это «народное творчество» интересовалось соблюдейнием авторского права чуть менее, чем «никак». Было проще всё выкинуть и заново написать, чем концы найти и связаться с разработчиками компонентов!Интересное замечание. Получается, то, за что (среди прочего) хвалят паскаль (полноценные модули, честная раздельная компиляция, стабильный ABI, вот это всё) его и погубило?
Получается, то, за что (среди прочего) хвалят паскаль (полноценные модули, честная раздельная компиляция, стабильный ABI, вот это всё) его и погубило?Во многом — да. Наши недостатки — продолжение наших достоинств.
Хотите другой продукт, который постигла та же участь? Windows.
Она прочно оккупировала десктоп, так как под неё есть куча всего, буквально миллионы программ (если учесть всякие внутрифирменные поделки). Но… это всё бинарники. Под десктоп. Потому никакие попытки вырваться с этого плацдарма ну хоть куда-нибудь (многочисленные версии Windows NT под разные RISC процессоры, мобильники и прочее) — к успеху не привели.
Погубило Delphi другое. То, что модули можно было распространять в виде бинарников.
Хм, нет. Это как раз фича. Нет форсированного оперсорса, хочешь — публикуй исходники, не хочешь — нет. Тем более, что проприетарные коммерческие компоненты обычно с исходниками поставляются.
Та же система компиляции бинарников (более удобная, чем в delphi) есть в Java. И Java это не погубило. (C#, насколько я понимаю, аналогично)
Если рассуждать о том, что погубило delphi — имхо, это
1) бардак в синтаксисе, одних массивов несколько типов, и между собой они не очень совместимы; классы и интерфейсы (используются для ARC) — это раздельные, не всегда совместимые между собой сущности; и т.д., и т.п.
2) язык отстаёт от прочих. generic'и завезли поздно и криво, синтаксис для лямбд ужасно многословен.
3) ГЛЮЧНАЯ IDE, начиная после delphi 7, а по воспоминаниям олдфагов — с delphi 7. d2007, если не ошибаюсь, могла упасть 5-6 раз за 40 минут на сложных проектах. Что до последней версии IDE, она не может корректно подсвечивать ошибки и форматировать более-менее сложный код с лямбдами. Хотя разработчик IDE и языка — одна контора.
4) убогая стандартная библиотека даже в последних версиях, забагованная нестандартная (jcl).
5) и это всё платно, за довольно большие деньги (в несколько раз дороже, чем visual studio). Нелицензионное использование детектируют и пишут письма счастья.
6) таки есть проблемки при переходе между версиями, но где их нет?
таки есть проблемки при переходе между версиями, но где их нет?
С первой на вторую, со второй на третью, с третьей на четвёртую, с четвёртой на пятую — вообще проблем не было.
Хм, нет. Это как раз фича. Нет форсированного оперсорса, хочешь — публикуй исходники, не хочешь — нет.И в ситуации, когда весь софт покупался на лотках в переходе за три копейки большинство разработчиков исходниками не заморачивалось.
Та же система компиляции бинарников (более удобная, чем в delphi) есть в Java. И Java это не погубило. (C#, насколько я понимаю, аналогично)Пока не погубило. У них пока не было необходимости «сломать» совместимость при переходе на новую платформу. Потому что то, что было сделано изначально — покрывало все современные платформы и поддержка всяких MIPS'ов/Sparc'ов шла из кородки (а ничего нового как и нету).
Если вдруг Java или C# попадут в ситуацию, когда им придётся сломать совместимость — будет то же самое, что и с Delphi (или Windows, как я описывал выше).
Принципиально разных версий .NET Framework всего четыре: 1.0, 1.1, 2.0 и 4.0, причем первые две давно мертвы. Все остальные версии — обновления для какой-то из этих.
А в Java разве есть совместимость?
Как правило, библиотека, скомпилированная под что-то древнее, нормально запускается (запускалась) на новых версиях без перекомпиляции. С приходом java9 некоторые (использующие грязные хаки jvm) библиотеки отваливаются.
Скорее всего дело в том, что Борланд изначально европейская (преимуществено Датская) компания с филиалом в США, ну и ушлые янки в результате совестливых интеллигентов отправили домой, а кассу оставили себе. Какого развития средств разработки можно ожидать в итоге?
Не все эту историю знают, но обычные разработчики и менеджеры ещё тогда, в 95 году, почувствовали, что дел иметь больше не с кем, перемены в стратегии и даже в названии, как в погоде, семь пятниц на неделе, нет больше серьёзной компании на которую можно делать ставку. А жаль, это до какой-то поры был редкий пример настоящего успешного европейского менталитета в нашей индустрии. Ван Россум, кстати, другой такой пример.
object _value;
public object Value => _value ?? (_value = whatever);
(да, я знаю про Lazy, и нет, его нельзя или неудобно использовать в большинстве случаев)
Есть еще LazyInitializer
with file := open('filename'):
chunk = file.read('8192')
with open('filename') as file:
chunk = file.read('8192')
Кстати, как по мне, в циклах и условиях «as» выглядел бы более питонично, а в list comprehensions оба варианта здорово сбивают с толку.
if pattern.search(data) as match is not None:
while file.read(8192) as chunk
process(chunk)
[f(x) as y, y**2, y**3]
filtered_data = [y for x in data if f(x) as y is not None]
P.S. PEP не читал. Надеюсь, у них были весомые аргументы против того, чтоб расширить область применения «as».
Вроде
def func(a=(x:=0), b=x):
return a+b
Очень долго возможность присваивания в цикле или проверки считалась багом и источником ошибок.
if (diff := a - b):
doSomething(diff)
и
if (diff == a - b):
doSomething(diff)
Без глубоко и громоздко:
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
else:
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
rv = reductor(4)
else:
reductor = getattr(x, "__reduce__", None)
if reductor:
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
С линейно и прозрачно:
if reductor := dispatch_table.get(cls):
rv = reductor(x)
elif reductor := getattr(x, "__reduce_ex__", None):
rv = reductor(4)
elif reductor := getattr(x, "__reduce__", None):
rv = reductor()
else:
raise Error("un(shallow)copyable object of type %s" % cls)
А ещё теперь можно писать быстрые генераторы comprehension:
[y for x in data if (y := long_running_fucn(x)) is not None]
Раньше нужно было либо делать цикл, либо вызывать функцию дважды.
reductor = dispatch_table.get(cls)
if reductor:
rv = reductor(x)
когда можно
if dispatch_table.get(cls):
rv = dispatch_table.get(cls)(x)
Пример
[y for x in data if (y := long_running_fucn(x)) is not None]
про то же, он позволяет делать действия посчитать-проверить-использовать коротко.
Там, кстати, есть ещё фича с использованием в all() и в any(), которая позволяет вытащить элемент, который заваливает all() или разрешает any() опять же без написания цикла.
Вот ещё типичная ситуация:
check = foo()
while check:
action(check)
check = foo()
превращается в
while check := foo():
action(check)
Вероятно для того, чтобы работало в 2 раза быстрее.Про два раза вы мягко говоря преувеличиваете.
Большую часть времени будет занимать вызов функции (который гораздо тяжелее, нежели dict.get). Можно это переписать как.
if cls in dispatch_table:
rv = dispatch_table[cls](x)
In [1]: %%timeit d = {'ok': lambda: None}
...: r = d.get('ok')
...: if r: r()
...:
The slowest run took 22.09 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 131 ns per loop
In [2]: %%timeit d = {'ok': lambda: None}
...: if 'ok' in d:
...: d['ok']()
...:
The slowest run took 23.52 times longer than the fastest. This could mean that an intermediate result is being cached.
10000000 loops, best of 3: 84.5 ns per loop
reductor = dispatch_table.get(cls)
if reductor:
return reductor(x)
reductor = getattr(x, "__reduce_ex__", None)
if reductor:
return reductor(4)
reductor = getattr(x, "__reduce__", None)
if reductor:
return reductor()
raise Error("un(shallow)copyable object of type %s" % cls)
Сразу видно повторение структуры
reductor = some_get_reductor_strategy()
if reductor:
return reductor(something_or_nothing)
Которую частично можно заменить nullable object'ом
def dummy_reductor(*args): pass
result = dispatch_table.get(cls, dummy_reductor)(x)
if result:
return result
result = getattr(x, "__reduce_ex__", dummy_reductor)(4)
if result:
return result
result = getattr(x, "__reduce__", dummy_reductor)()
if result:
return result
raise Error("un(shallow)copyable object of type %s" % cls)
И избавиться от повторяющихся if'ов
def dummy_reductor(*args): pass
result = (
dispatch_table.get(cls, dummy_reductor)(x) or
getattr(x, '__reduce_ex__', dummy_reductor)(4) or
getattr(x, '__reduce__', dummy_reductor)()
)
assert result, 'un(shallow)copyable object of type %s' % cls
return result
Уверен, что код можно еще упростить, если знать контекст.
В list comprehension новая фича выглядит полезной, пока не приходит осознание, что при реальной необходимости её можно реализовать самостоятельно один раз на весь проект:
def transform(iterable, with_transform, and_filter):
result = []
for item in iterable:
item = with_transform(item)
if and_filter(item):
result.append(item)
return result
result = transform(
data,
with_transform=long_running_func,
and_filter=lambda x: x is not None
)
result = transform(data, long_running_func, lambda x: x is not None)
Ваш вариант с transform выглядит не pythonic-way, и адекватный разработчик никогда не пропустит такое на ревью
def foo():
for item in data:
item = long_running_func(item)
if item is not None:
yield item
result = list(foo())
Без введения новых абстракций, без необоснованного использования лямбд, без создания и модификации временного объекта (списка)
Не, я конечно понимаю, что можно писать на питоне как на фортране/подставь-свой-вариант, но не в том случае, если его будут поддерживать другие люди
P.s.
result = list(filter(None, map(long_running_func, data)))
transform
использовался вместо нового синтаксиса в list comprehension во всем проекте. То есть предполагалось переиспользование этой функции, а значит нужно либо вводить новую абстракцию, либо везде использовать последний вариант:result = list(filter(filter_func, map(long_running_func, data)))
Я согласен, что абстракция не нужна, если проблема, которую решает эта абстракция, не сильно распространена в коде. Просто я исходил из предположения, что проблема сильно распространена.
Жаль, в python у коллекций нет методов map и filter, чтобы пример выше выглядел читабельнее:
result = data.map(long_running_func).filter(filter_func)
Не могу сказать, что я хотя бы раз сталкивался с ситуацией, в которой жалел о том, что такой фичи нет в Питоне.
Я не раз ловил себя на мысли, что хочу такую штуку в comprehension, еще до появления данного pep
Большое количество примеров есть на гитхабе https://github.com/python/cpython/pull/8122/files
вижу смысл только, для while
А вообще, спасибо за своевременную публикацию новости. Грустно, все стареют, того и гляди, скоро и Торвальдс на пенсию уходить соберётся… Ужс.
Спасибо за статью — узнал для себя создателя Питона.
Даже не знаю, что сказать. Я не очень пристально слежу за разработкой языка, но честно говоря, я всегда любил питон за некоторую строгость и лаконичность. Мне кажется, что данное расширение не идет на пользу языку, так как позволит писать трудно понимаемые конструкции с большим потенциалом для возникновения ошибок.
При этом странно: многострочные lambda функции не допускаем, а подобное безобразие не только пропихиваем в язык, но еще и недовольны, что не всем это нравится.
if (
(
(
(a := F1()) and (b := F2(a)) > 100
or (a := F0()) and (b:= F3(a, 0)) > 100
)
and ( (c := F2(a, b)) or (c := F3(a, b)) )
and ( res := F4(a, b, c) )
or (
(a := G1()) > 100 and (b := G2(a))
or (a := G0()) > 100 and (b:= G3(a, 0))
)
and ( (c := G2(a, b)) or (c := G3(a, b)) )
and ( res := G4(a, b, c) )
)
and
(
(res := F5(res, a, b, c)) > 1
or (res := G5(res, a, b, c)) > 1
)
):
do_something_with(res) ...
перепишите на "строгий" питон что-нибудь такое, и посмотрите потом как "лаконично" у вас получится.
А я такие полуфункциональные конструкты и почище этого видел.
При том не забываем, что тут на самом деле не a,b,c и Fx, Gx (как тут для наглядности), а длинные говорящие значения.
Я часто не согласен с решениями Гвидо, но тут его полностью поддерживаю…
Это как минимум просто удобно (и очень часто нехватало в питоне), спасает от дублирования кода и временных ненужных переменных, ну и тупо необходимая мера, если питон и дальше должен считаться близким к функциональным языком.
позволит писать трудно понимаемые конструкции
Это вот-это? Вы серьезно. Какой-нибудь трех-этажный взаимосвязанный декоратор (который даже его разработчик через пол-года не понимает) — это "позволяется", а оператор (оператор, Карл!) всё испортит видите ли… Абракадабру можно написать любыми конструктами в любом языке, это дело личной гигиены и дисциплины (и возможно попустительства ревьюверов, коммьюнити и т.д.). Но никак не отдельного оператора.
недовольны, что не всем это нравится.
Всем это простите кому? У вас есть статистика? Многим — возможно, но в моем окружении, это например "всем" поголовно нравится. И что?
И потом, тут всё просто, не нравится — не используйте...
перепишите на "строгий" питон что-нибудь такое, и посмотрите потом как "лаконично" у вас получится.
Да оно и тут не особо как читаемо, не говоря о том, что дебажить if со сложной иерархией 10 различных состояний — это такое себе… те, кто такой код будут поддерживать — добрым словом автора не вспомнят.
И потом, тут всё просто, не нравится — не используйте...
Если бы все так просто было. Я вот использовать не буду (условно, лично я не против), а мои коллеги чей код мне потом разбирать — будут. От таких вещей так просто не отгородиться к сожалению.
А я такие полуфункциональные конструкты и почище этого видел.В приличном обществе это называют «говнокод», но не «полуфункциональные конструкторы.
ну и тупо необходимая мера, если питон и дальше должен считаться близким к функциональным языком.Обычно чем функциональнее язык, тем реже в нем используется оператор присваивания. В чистых языка (напр. Haskell), оператора присваивания вообще нету… Но да, для Python это прям необходимоя мера, ага :D
Или вы про ФП, где избегают мутирующих присваиваний (а :=, на минуточку, именно такой)?
Столько эмоций вокруг деталей синтаксиса тупо не соизмеримы с эффектом от введения собственно этого оператора.
Если у человека, после многия доказательства, аргументов и примеров… всего один контр-аргумент типа «говно-код», то вести с ним дискуссию на академическом уровне считаю ниже своего достоинства.
Такой ответ вас устроит.
Столько эмоций вокруг деталей синтаксиса тупо не соизмеримы с эффектом от введения собственно этого оператора.Ну какбы да. Совершенно непонятно, зачем такую фичу пропихивать «против сообщества».
Если у человека, после многия доказательства, аргументов и примеров… всего один контр-аргумент типа «говно-код», то вести с ним дискуссию на академическом уровне считаю ниже своего достоинства.Другие примеры неплохи. Хотя из всех только один видится состоятельным — 'while chunk := ...'. Все остальное без особой потери читабельности переписывается без ':='.
Насчет вашего примера… Как вам уже заметили «кто такой код будут поддерживать — добрым словом автора не вспомнят». Поэтому я настаивал и буду настаивать, что этот пример можно и нужно называть
Синтаксис и семантика ЯП — это ограничения, которые направляют мысль программиста и облегчают её путь. Если они направят мысли людей не туда (а с новичками так точно будет), то через несколько лет будут большие проблемы у всех.
Сейчас есть два примечательных ЯП, которые можно считать показательными примерами пренебрегания «деталями синтаксиса»: Perl и JavaScript. Синтаксические фичи обоих давно и прочно обосновались в фольклоре. Perl сообществу так и не удалось починить и он медленно умирает. JavaScript уже который год пытаются привести в нормальный вид и не ясно получится ли. И оба этими «фичами» доставили массу проблем разработчикам.
Мой ответ был конкретно на «лаконичность» и «читаемость». Сделать именно такие вещи (хоть ребус, ходь код) более читаемымы и/или лаконичнымы (даже с введением новых сущьностей) не очень получается по видимому…
Поэтому и контр-аргументы такие… вида «говнокод» и «ребус». Ну-ну…
if((a := F1()) and (b := F2(a)) > 100 or (a := F0()) and (b:= F3(a, 0)) > 100):
if((c := F2(a, b)) or (c := F3(a, b))):
if(res := F4(a, b, c)):
do_something_with(res)
elif((a := G1()) > 100 and (b := G2(a)) or (a := G0()) > 100 and (b:= G3(a, 0))):
if((c := G2(a, b)) or (c := G3(a, b))):
if(res := G4(a, b, c)):
if((res := F5(res, a, b, c)) > 1 or (res := G5(res, a, b, c)) > 1):
do_something_with(res)
?
И сразу начинают терзать смутные сомнения насчет условия: ((res := F5(res, a, b, c)) > 1 or (res := G5(res, a, b, c)) > 1). А все ли там так.
Логика такого масштаба развесистости не имеет права быть реализованной в одном методе. Просто посмотрите на количество "компонентов" — a, b, c, F0, F1, F2, F3, F4, do_something_with(), res, и т.д. Если внутри одного метода действительно такой богатый контекст, это означает, что проблема не в самом методе, а в его окружении. Будет этот метод таким как вы его написали (с новым оператором) или в 3 раза длинее (без нового оператора), разницы совершенно никакой: он хренов бай дизайн.
res = 0
if not res:
a = F1()
b = a and F2(a)
if not (a and b > 100):
a = F0()
b = a and F3(a, 0)
if a and b > 100:
c = F2(a, b) or F3(a, b)
res = c and F4(a, b, c)
if not res:
a = G1()
b = a > 100 and G2(a)
if not (a > 100 and b):
a = G0()
b = a > 100 and G3(a, 0)
if a > 100 and b:
c = G2(a, b) or G3(a, b)
res = c and G4(a, b, c)
if res:
res = F5(res, a, b, c)
if res <= 1:
res = G5(res, a, b, c)
if res > 1:
do_something_with(res) # ...
А главное стало так похоже на изначальное математическое-выражение.
Т.к. в комментарии тяжело выразить все эмоции — это был сарказм если что.
А главное стало так похоже на изначальное математическое-выражение.
Это в каком таком разделе математики используется деструктивное присваивание?
Ни в каком (ну почти), просто когда F2(a)
может внезапно делить внутри на a
(что есть результат от F1
), нужно вдруг делать в питоне a and F2(a)
чтобы не словить исключения типа ZeroDivisionError.
Но выворачивать всё на изнанку и плодить лишние сущности и проверки в угоду "строгому" синтаксису языка (и с потерей читабельности кстати) — это простите нонсенс.
if a == 0:
return 0
ну или
if a == 0:
return None
Вы не поняли мой: я прекрасно знаю для чего assert нужен, но только caller знает что в общей формуле должно проверяется, если результат F1 должен быть в общей формуле больше 20 например, то будет двойная проверка внутри F2 на 0 и в if на > 20.
Результат же один — оппоненты придумывают оправдания и способы обхождения ограниченного синтаксиса…
Изначально математическое выражение неизвестно, понять что ваш код делает сложновато… Поэтому я переписал его без :=, сохранив структуру.
Но да, я считаю что оно стало читабильнее. По крайней мере легче читаются guard-предикаты вида «a and b > 100». Очевиднее как вынести куски в отдельные функции ит.п.
def ab1():
a = F1()
b = a and F2(a)
if a and b > 100:
return a, b
a = F0()
b = a and F3(a, 0)
if a and b > 100:
return a, b
return None, None
def ab2():
a = G1()
b = a > 100 and G2(a)
if a > 100 and b:
return a, b
a = G0()
b = a > 100 and G3(a, 0)
if a > 100 and b:
return a, b
return None, None
res = 0
if not res:
a, b = ab1()
if a and b:
c = F2(a, b) or F3(a, b)
res = c and F4(a, b, c)
if not res:
a, b = ab2()
if a and b:
c = G2(a, b) or G3(a, b)
res = c and G4(a, b, c)
if res:
res = F5(res, a, b, c)
if res <= 1:
res = G5(res, a, b, c)
if res > 1:
do_something_with(res) # ...
PS: Кстати, это так задумано, что функции F2 и G2 могут принимать как 1 аргумент, так и 2?
Но да, я считаю что оно стало читабильнее.
Ну-ну, а что нынче в универах перестали курс булевой алгебры преподовать?...
И главное-то мой первый комментарий был ответом на "лаконичнее"...
это так задумано, что функции F2 и G2 могут принимать как 1 аргумент, так и 2?
А это вас удивляет, в языке как python… def F2(x, factor=1)
Ну-ну, а что нынче в универах перестали курс булевой алгебры преподовать?...Не могу уловить ход ваших мыслей.
И главное-то мой первый комментарий был ответом на «лаконичнее»...Если под лаконичностью вы понимаете сугубо размер кода, то в моем варианте аж на 14 непробельных символов больше (не поленился и посчитал). С этой точки ваш исходный код более лаконичен :D
А это вас удивляет, в языке как python… def F2(x, factor=1)Меня удивляет то, что вы заявляли о «похожести на мат выражение». В мат выражении у вас у функций тоже дефолтные параметры есть? :D
Не могу уловить ход ваших мыслей.
"стало читабельнее"… булева алгебра по видимому для вас не читабельна. Отсюда и вывод.
Если под лаконичностью вы понимаете сугубо размер кода
Под лаконичностью я понимаю то же что и все люди (см. определение в словаре). Это стало менее лаконично. Точка.
Под лаконичностью я понимаю то же что и все люди (см. определение в словаре). Это стало менее лаконично. Точка.Согласен.
Правда изначальный ваш посыл был «теперь это конечно много легче поддерживать и развивать». Согласен, теперь много легче.
Наговнокодить можно в любом случае. Если использовать := разумно, то иногда можно хорошо улучшить ясность кода
filtered_data = [ z.group(1) if z := re.match(y) else None for x in data if (y := f(x)) is not None]
Хотя тут тоже можно оговаривать ограничения в правилах кодирования: не больше одного сравнения-присваивания в выражении.
об этом же хотел написать :)
Питон без этого оператора будет называться True Python (рус.: ПитонЪ) ;)
---камент не туда. как удалить?
почему-то только в течение пяти минут — почему не хотя бы сутки?
Для того, чтобы тролли/спамеры не использовали эту фичу в целях провокации.
да я даже отредактировать не могу… причём сразу же после публикации… и тут 5 минут мне бы вполне хватило
Совершенно не понимаю, почему принимают относительно незначительную фичу, в условиях такого количества разногласий в сообществе.
Даже без учёта самой фичи, решение о принятии само по себе не Python way.
Мы, к примеру, в продакшене выжимаем из питона то, что не все могут выжать из Go и ноды.Статью про это не хотите написать? Уверен, многим будет интересно.
в продакшене выжимаем из питона то, что не все могут выжать из Go и ноды
Если вы умудрились уделать интерпретируемым языком с GIL конкурента без этих ограничений, то мне кажется, что в этом соревновании выступали: Senior Python vs Junior Go.
Громкое заявление. А можно примеры кода, которые у вас оказались столь быстры?
Какие вообще препятствия есть и почему уход Гвидо на это влияет?
(на Питоне не пишу, потому спрашиваю серьёзно, так как думаю об его изучении)
Что касается обдумывания учить его или нет, то оно точно не должно зависеть от GIL. Вам надо отталкиваться от задач, а не от ограничений языка. Есть задача -> есть условия -> есть язык -> обучение.
Linux-kernel на очереди.
Без принципиального и честного лидера любой OpenSource-проект паскудится корпорастами.
Надеюсь, Торвальдс не сбежит так трусливо, а создаст перед этим правила принятия и выпинывания коммитеров, а также жизнеспособные правила выбора диктатора.
а от всяких proj и прочее — никогда обратная совместимость не требовалась.
Уж настроить среду заново при переходе на новую версию — не проблема.
Возможно, имеют в виду переход на Unicode String в Delphi 2009, когда старый тип String (который раньше обозначал строки из 1 байтных символов) начал обозначать строки из 2-х байтных символов. Но при этом Embarcadero очень сильно потрудилась, чтобы облегчить миграцию. Кто не осилил — тот просто плохой разработчик.
P.S>
Это шутка! ШУТКА
На днях мы получили патчи Интела для OpenWRT. Как вы могли догадаться — опаскуженные. Какое счастье, что они не имеют привычку обратно контрибьютить к отцам-основателям. Думаю, тоже самое и в мире форков FreeBSD.
Ну вообще Интел один из главных коммитеров в ядро Linux.
Или это у меня детектор сарказма сломан?
Complete and utter garbage.
Подробнее вы можете посмотреть тут.
Поверьте мне, хуже кода, чем делает Intel в мире пока еще не придумали. Это огромная машина индусских разработчиков, которая сметает все живое на своем пути.
Скриптовому языку не нужно развиваться бесконечно. В принципе, никакому языку не нужно, но рантаймы серверных языков участвуют в бесконечной гонке за производительность, поэтому разработка необходима, чтобы не остаться на обочине истории.
Зачем многопоточность в скриптовом языке? Возьмите уже нормальный серверный язык...
Casual числодробилка (пример: какая-нибудь аналитика), которая не ложиться на готовый алгоритм в numpy.
Интересно, а обычное присваивание для чего тогда нужно будет? Везде же можно будет использовать :=
?
Не везде. На верхнем уровне := без скобок запрещён, для именованных параметров остаётся =. Там специально оставлено очень мало выбора в каждой отдельной ситуации.
- Обычное присваивание — 1 символ, новое два.
- Обычное присваивание — тонна существующего ПО.
- К тому же сделано всё так, что := используется когда вы знаете что делаете и зачем Вам нужно возвращаемое значение.
а, кого я обманываю, OSM так и катится в бездну.
9 Кикать — исключать из любого онлайн сообщества, лишать членства.
Да ну? А просто написать «исключать» — никто бы не понял?
3 Менторить — обучать, подсказывать, помогать начинающим во всём разобраться.— ну, спасибо вам за словарь!
Ну и горутины просто шикарны, заценил, так как много потребностей в этом условно параллельном выполнении.
Переход с Python на Go звучит очень странно.Это даже Роба Пайка удивило. Он этого не ожидал.
На Go можно перейти с C или Rust — это еще куда ни шло.А вот и нифига. Это может показаться удивительным, но несмотря на массу формальных различий между Go и Python'ом и формального сходства между C/C++/Rust'ом и Go — на практике Go и Python «живут» в одной нише, а C/C++/Rust — в другой.
Грубо говоря ниша Go и Python — это «мне нужно быстренько маленькую такую приблудку/скриптик сбацать», а C/C++/Rust — это «мне нужен компонент в нашу монструозную мегахрень на 100 миллионов строк кода».
Потому что хотя формально Go и Python могут быть вкручены в другой процесс — но на практике это серьёзная проблема. У них свой, достаточно тяжёлый, рантайм, своё «видение мира» и так далее. Добавление Go и Python'а в проект не написанный на Go/Python — это большое дело.
А вот модуль на C/C++/Rust — можно «прикрутить» к программе на Java, хоть на C#, а можно и на том же Python'е. А вот писать всё программу на C/C++/Rust — часто весьма грустно.
Но это всё теория. Практика же такова: люди гораздо чаще переписывают на Go вещи, которые ранее были написаны на Python'е, чем вещи, ранее написанные на C/C++ (Rust пока слишком молод, чтобы говорить о переходе с него куда-нибудь).
Мотивация создателей Го была сделать язык для больших и огромных корпоративных тимов, с бюрократическим подходом в разработке, то есть в нишу Явы, но нативный.Откуда сведения??? Языков для «копоративных тимов» у Googlе было и есть два: C++ и Java.
А вот для специфичных вещей было создана куча специализированных же языков (самый известный из них Sawzall. Но то, что каждый из них со временем старался развиваться — и появилась идея всё это заменить на один какой-то язык. Который, во-первых, не требует для компиляции часов, а во-вторых — не порождает в результате бинарники на сотни мегабайт.
Сахар в Го это уже импровизация, заказчику было всё-равно, будет Го похож на Питон, Оберон или на Яву.«Заказчиком» был как бы Роб Пайк, написавший некоторое количество вот этих самых мини-языков. Потом и другие присоединились…
Rust пока слишком молод, чтобы говорить о переходе с него куда-нибудь
По-моему, больше стоит вопрос о переходе на Rust.
Rust и Go концептуально появились почти одновременно. Только скорость развития и достигнутые результаты на данный момент сильно отличаются. На Go чего только не пишут от мелких одноразовых программ до крупных систем. На Rust почти ничего нет.
По-моему, больше стоит вопрос о переходе на Rust.Сейчас — таки да.
Rust и Go концептуально появились почти одновременно.Они как бы «заявили о себе» одновременно (да и то — Go в 2009м, а rust — в 2010м). А вот версии 1.0 (после которых только и можно говорить о внедрении) — появились с разницей в три года. Go — в 2012м, Rust — в 2015.
На Go чего только не пишут от мелких одноразовых программ до крупных систем. На Rust почти ничего нет.Примерно как на Pascal в начале 80х. Тогда и универсайная переносимая среда и первая графическая операционка и много-много всего были на «языке будущего» написаны. Правда кое-то предпочитал Modula-2. И Microsoft и всякие компинии типа Watcom'а именно под него всё делали. И все было уверены, что близкий родственник — это то, куда пойдёт индустрия…
А вот подиж ты: вышло-то по другому.
На Go чего только не пишут от мелких одноразовых программ до крупных систем. На Rust почти ничего нет.Всему своё время. Если Rust реализует свою задумку и на нём будут писаться модули к C/C++ программам — временный успех Go окажется таким же временным как и временный успех Pascal'я. А если нет — значит нет.
Так то и С++ продолжает развиваться. Даст ли раст такие преимущества, чтобы в него пошли массы разработчиков…
Даст ли раст такие преимущества, чтобы в него пошли массы разработчиков…Он-то уже даёт. Просто у Rust очень высокий порог вхождения и наверное он этим пугает.
И много разных нюансов. Например, в Rust одних только строк 8 видов: {utf-8, c-compatible, os-specific, file system paths} × {borrowed, owned}.
Раньше, когда Rust был в своих первых 1.х версиях, было сложно: и библиотек было очень мало, и всевозможных примеров, гайдов, туториалов. Сейчас эко-система вокруг набирает обороты, и потому уже гораздо проще.
Но для опытных разработчиков на С/С++ должно же быть нормально? Или и для них высоко?Опытным разработчикам на C/C++ и без того есть чем заняться.
Опытные разработчики очень обижаются, что компиляторы не дают стрелять по ногам. Ну вот например, с одной стороны человек делает Option<T>
(то есть явно говорит, что значения может не быть), а потом говорит "эх, без анврапа значения не молучить, а я ж точно знаю, что оно там есть".
У меня на самом деле складывается такое ощущение, что новый человек с этими концепциями сживается проще, чем человек с бекграундом С++. Например, я в своих проектах рассматриваю раст как замену моему основному C#, и мне концепции вроде владения дались сильно проще, чем об этом пишут в комментариях специалисты по С++. А ведь это язык с GC где не надо думать про память… Ой, а у нас же есть файлы. А еще сокеты. А еще куча всяких других ресурсов, про который GC не знает и которые приходится костылить через IDisposable. А потом наворачиваем сверху DI чтобы совсем было все приятно.
И поэтому, когда я переписываю код на Rust, где борроу чекер проверит все зависимости, где нет разделения "объект с ресурсами/без ресурсов", где я не получаю NullReferenceExeption в рандомных местах, и где всё приправлено бесплатными итераторами (сколько кушает LINQ можно посмотреть тут) и удобными штуками вроде impl<T> Foo for T
, то я испытываю огромное облегчение и удовольствие. И при этом не было какого-то отторжения. Ну да, нужно понять, что есть определенные правила взаимодействия с объектами, но и гарантии, которые появляются в результате этого того стоят. Нужно просто не быть зашореным в имеющемся опыте, а попробовать взглянуть на проблему с другой точки зрения.
Простите, но как автор того комментария, на который вы сослались, вынужден поздравить вас соврамши. Я такого не говорил. Речь шла о том, что если вы в интерфейс своей функции засунули Option, то достать значение из Option-а внутри функции без проверки вы не можете. Поэтому Option в низкоуровневом коде, от которого требуется производительность, вы сможете использовать далеко не везде.
И я очень надеюсь, что здесь сказывается ваше поверхностное знакомство C++ (поскольку вы C#-пер, а не C++ник), а не желание передергивать.
Простите, но как автор того комментария, на который вы сослались, вынужден поздравить вас соврамши. Я такого не говорил. Речь шла о том, что если вы в интерфейс своей функции засунули Option, то достать значение из Option-а внутри функции без проверки вы не можете.
Да. Если вы засунули в интерфейс функции Option, значит он вам может прийти и проверка необходима. Option я буду использовать везде, где он необходим, включая низкоуровневый код. Потому что наприме при передачи референса Option соптимизируется до проверки на null, которую вы бы а С++ в любом случае писали бы. По крайней мере я надеюсь, что писали бы, а не «Да тут null никогда не будет», когда интерфейс функции прямо говорит, что null прийти может.
(поскольку вы C#-пер, а не C++ник)
C#-пер звучит как ругательство :)
И я все еще не понимаю, с чем вы спорите. Если у вас в интерфейсе написать «может прийти null», то функция обязанапроверять на null, нет?
Я даю прямые цитаты.
Язык отношения не имеет. Я не рассуждаю про какие-то особенности указателей или нужен ли буст. Я обуждаю абстрактные вещи, которые понятны и на псведокоде: если функция принимает тип T?
, должна ли она бездумно кастовать её в T
? Почему тогда не сделать T
сигнатурой?
Я даю прямые цитаты.Не вижу. Зато виду выдачу своих фантазий за мои слова. Т.е. вранье.
Я обуждаю абстрактные вещиСлишком абстрактные. Если говорить предметно, то у вас может быть либо так:
fn do_A(v: &Option<Something>) {...}
fn do_B(v: &Option<Something>) {...}
fn do_something(v: Option<Something>) {
do_A(&v);
do_B(&v);
}
и тогда у вас проверки будут во всех do_*. Либо можете сделать так:
fn do_A(v: &Something) {...}
fn do_B(v: &Something) {...}
fn do_something(v: Option<Something>) {
if let Some(value) = v {
do_A(&v);
do_B(&v);
}
}
И проверка будет только в do_something. Если вы пишете низкоуровневый и производительный код, то вы вряд ли выберете первый вариант и, скорее всего, предпочтете второй, в котором у вас Option будет всего в одном-двух местах. Тогда как основная часть кода будет без Option.
Именно это я вам в который раз пытаюсь объяснить.
Это зависит исключительно от того, что внутри do_a
/do_b
.
Если они логически должны обрабатывать значения Option, то там должны быть Option. Если нет — то нет. И скорее всего будет именно второй вариант, потому что единственный сценарий, при котором подобное может произойти — мы пишем какой-то обработчик, трейт которого вынуждает нас использовать Option и no-op в случае, если значения нет. В случае do_a
и do_b
таких ограничений нет, потому что это будут приватные функции.
Та же претензия что и к питону — однопоточность. Да, есть файберы, но это одно ядро, и все что из этого следует. Ждать, пока ситуация изменится, я пока не готов. Например глядя на ocaml, который появился в 1985 году, а попытки добавить в него полноценную конкурентность стали предприниматься совсем недавно. Хотя сам язык, на мой взгляд, весьма интересный и достоен большего внимания, чем сейчас реально имеет.
VsCode предлагает какие — то плагины для поддержки crystal, это очень хорошо.
В общем: спасибо за наводку, буду пробовать, что за зверь, и с чем его едят, хотя бы для того, чтобы составить свое представление.
Просто мне кажется, с тех пор синтаксис не изменился сильно. Разве что добавили, например, оператор
?
, который заменил макрос try!
(меньше скобок и визуального мусора), а ещё (из недавнего) — impl Trait
и &dyn Trait
.Полистайте растоновости на том же ЛОРе (типа такой), там в обсуждениях народ часто об этом пишет.
&8
, let
и обработке результатов через Result
— ну как-то…Да, я понимаю, если синтаксис в целом не нравится субъективно. Тут уж либо пробовать на нём писать и привыкать (я лично раза 4 его бросал, но всегда появлялось желание вернуться), если нравятся довольно строгие гарантии безопасности, либо забить и пользоваться чем-то другим (никуда не денется C, ещё народ Go любит, хотя они с Rust вроде бы не конкурируют).
Ну давайте разберем.
fn main() {
let x = 1;
let y = {
x+1;
};
println!("x={:?}, y={:?}", x, y);
}
Тут автор жалуется, то точка с запятой меняет семантику. Да вы что :) А в C++ с его while (cond); {}
это совсем не так. И что любопытно, на эти грабли наступают примерно с одинаковой частотой, а именно один раз при обучении.
У концепции "всё expression" в сочетании вывода типов подобные вещи влияют, да, но как видно, и в statement-выражениях подобные вещи применимы. Можно придраться к тому, что вместо ошибки компиляции выводится Unit
, но это уже попытка выдать плюс за минус. К этому быстро привыкаешь, и это становится намного более удобно. Например вернуть два значения из разных веток if. Да, в C++ есть ?:
оператор, но как только нам нужно сделать какое-то локальное вычисление (или например просто сделать запись в лог branch1-branch2), то сразу встает вопрос — что делать? Обычно тогда переменные объявляют до условия, а внутри просто устанавливают значение. Чем это чревато, думаю, объяснять не надо.
По второй в основном жалуется, что все символы важны. Ну например:
pub fn foo(a: &[u8], b: &[u8], c: &mut [u8]) {
если объясните, в чем сложность этой записи я буду признателен. Пока что я вижу просто функцию, которая принимает 2 readonly-слайса произвоильной длины a b и что-то делает с мутабельным слайсом c.
Ну и последний пример
fn new<'a: 'f>(foo: &'f Foo<'a>) -> Self {
let foo1: &'f Foo<'f> = foo;
Context { foo: foo1 }
}
такого кода в реальности вообще не встречал, лайфтаймы в 99% случаях элизится, а в остальных случая мне еще ни разу не приходилось использовать отношение a : b
. Но даже если, в чем проблема? Тут чутка генериков + касты. Да, пример страшноватый, но в реальности вы встретите это едва ли чаще, чем template-ошибку на 20000 строк.
такого кода в реальности вообще не встречалА этот кусок кода выдернут из контекста. В оригинале автор говорит: «which could be written more explicitly as», что как бы подразумевает, что по умолчанию так не пишут.
И из статьи же:
А что бы еще набросить на тему синтаксиса, вот прям скопирую фразу из анонса:Но автор либо не понял, либо решил умолчать, что это про старый синтаксис, от которого как раз решено постепенно избавляться.
Rust’s trait object syntax is one that we ultimately regret.Это из раздела про нововведение под названиемdyn Trait
.
В целом, ходит мнение, что Rust не нравится конкретно C/C++-никам. А те, которые приходят из динамических языков (типа JS, Ruby, Python), наоборот хавают Rust с удовольствием.
Python входит в состав Microsoft Visual Studio с 2015. Теперь Майкрософт будет «великодушным диктатором» развития языка.
Может, я чего не понимаю?
Вот пример использования Apache Spark, на Python и Scala:
Python:
text_file = sc.textFile("hdfs://...")
counts = text_file.flatMap(lambda line: line.split(" ")) \
.map(lambda word: (word, 1)) \
.reduceByKey(lambda a, b: a + b)
counts.saveAsTextFile("hdfs://...")
Scala:
val textFile = sc.textFile("hdfs://...")
val counts = textFile.flatMap(line => line.split(" "))
.map(word => (word, 1))
.reduceByKey(_ + _)
counts.saveAsTextFile("hdfs://...")
Что лаконичней?
В Python не устали писать «lambda» каждый раз, и ставить "\" в конце строки? Зачем это?
В scala лишнее слово Val перед переменной означает ее объявление и также то, что ее значение не может измениться в будущем.
(Издержки статической типизации, но это позволяет ускорить выполнение программ и предупреждать ошибки, по сравнению с питоном)
Далее вопросы к синтаксису:
for key in d:
d[key].sort()
В чем тайный смысл двоеточия, если по идее, должно быть достаточно отступов для формирования блоков кода?
Тоже и с оператором IF.
(Кто нибудь может сказать, что это для того, что бы писать операторы в той же строке, но часто вы в питоне такое видели? )
Далее идут классы.
class Some:
def __init__(self, id):
self.id=id
Мы что, на C++ пишем? Зачем двойные подчеркивания в языке высокого уровня?
«def init» никак не прошло бы? Давайте тогда и класс через "__class" объявлять, почему так не сделали?
Ну и напоследок о скорости питона.
Он до 50 раз медленнее scala.
Он значительно медленнее PHP, Javascript. Epic fail.
Скорость выполнения задач обспечивается библиотеками, написаными на C.
И не говорите о PyPy — это не стандатный питон.
Выводы:
Крайне медленный и весьма многословный язык, малоэстетичный.
Если его позиции пошатнутся, всем это только пойдет на пользу. (ИМХО, разумеется)
о скорости питона.
Он до 50 раз медленнее scala.
Он значительно медленнее PHP, Javascript. Epic fail.
А на чём держится его большая популярность?
Плюс было сразу умело создано комьюнити, распределены роли в руководстве, формализовали развитие языка, внедрились в университеты, в линуксы, создали менеджер пакетов, стали заниматься книгами, митапами, конвентами, короче люди работали.
Ещё не забывайте, что мы живем в эпоху бесплатных языков, а тогда даже примитивный интерпретатор Бейсика или компилятор Семантик С++ мог стоить сотню баксов и тут бесплатный Питон.
И вот как раз в сравнении с ними — у Python'а был и простой понятный синтаксис, и всякие другие плюшки.
Когда Python уже был (несколько лет), PHP еще было кучкой CGI скриптов на Perl и даже называлось PHP/FI (personal home pages / form interpretator), если кто забыл, а то потом адепты похапни стали этого («home pages») почему-то стыдиться и придумали, что PHP это теперь акроним.
Потом кучку скриптов переписали с Perl на C. И уже потом по сути PHP стало тем, чем его нынче поклонники и знают. Я имею в виду, что написали тройку (заодно избавились от букв FI в названии). Писали ее в 1997, из беты вышла, если мне не очень изменяет склероз, в 1998.
На python уже были тонны всего. Уже была прекрасная версия 1.5. Уже вовсю использовала наука (я познакомился с Python в университете, референсная реализация международного стандарта хранения результатов кристаллографических измерений была на Python, утвержденная международным конгрессом кристаллофизиков, на минуточку).
Господи, уже даже в вебе Bobo Publisher и Document Template были сложены в одну кучу и стали называться Principia, а до переименования в Zope оставалось полпальца (тот же 1998 год). Настоящий application server, объектное хранилище, traversal — в сравнении с кучками php скриптов это был в принципе другой уровень архитектуры, космос какой-то. Другое дело, что разработчики «домашних страниц» (тогда и корпоративные сайты частенько так назывались, так что никакого наброса) люто-бешено любили shared хостинги :)
И тут раз — на хабре пишут, что «рулил» PHP, а «потом появился» Python. Строго наоборот, когда Python уже рулил, PHP только появлялся. Не «появился», а именно «появлялся».
Что и позволило ему вытеснить Perl (а о Python в те времена задумывались разве что эстеты, писавшие инсталляторы или двигавшие науку).
А окончательное забвение Perl'а и отказ от PHP — это уже совсем недавняя история.
Тот факт, что PHP появился сильно позже Python'а и, в общем, долгое время был той ещё поделкой, не меняет того факта, что разработка прошла стадии Perl->PHP->Python, увы и ах… Да хотя бы на TIOBE взгляните:
1998й — Perl(2), Python(23), PHP пока нет
2003й — Perl(3), PHP(4), Python(11)
2008й — PHP(4), Perl(5), Python(6)
2013й — PHP(6), Python(7), Perl(8)
2018й — Python(4), PHP(8), Perl(12)
И тут раз — на хабре пишут, что «рулил» PHP, а «потом появился» Python.Кто пишет? Где пишет? Не надо только мне чужих слов приписывать, пожалуйтся. Я написал то, что написал… про появление Python'а после PHP и речи нет, речь о том, что долгое время Python был нишевым языком и только совсем недавно вошёл в мейнстрим.
Строго наоборот, когда Python уже рулил, PHP только появлялся.Увы, но нет. «Рулить» Python начал буквально в последние лет пять. А до этого — он был в том же списке, что и другие всякие полуэкзотические языки типа Scheme или Tcl. Кто-то пользовал Guilde для LilyPond, кто-то писал Portage на Python, а кто-то — Gitk на Tcl — кто во что горазд. А мэйнстримом были Perl и, позже, PHP.
Другое дело, что разработчики «домашних страниц» (тогда и корпоративные сайты частенько так назывались, так что никакого наброса) люто-бешено любили shared хостинги :)
Стоит тогда вспомнить, что тогда не-shared хостинги были очень редки и стоили в несколько раз (возможно, на порядок) дороже shared хостингов.
А Zope была мега-крутой штукой, просто космос, по сравнению со всем остальным, что тогда было. Но с определенными недостатками. Меня больше всего бесило редактирование TTW, которое в браузерах того времени просто не могло работать нормально. Потом появился ExternalEditor, но это было уже когда эпоха Zope начала клониться к закату.
Ну и ни один проект на Zope почти не взлетел, так как требовалась серьезная квалификация разработчиков. Особенно, когда пошли Plone и Zope 3. Документации толковой не было, книг тоже, все приходилось изучать по исходному коду.
Знакомый (не знаю, есть ли он на Хабре) когда-то сделал мощный портал на Zope 3, потом жаловался, что для поддержки и развития не удалось найти ни одного разработчика — слишком сложная архитектура.
Да что там говорить, до сих пор львиная доля сайтов делается на PHP и Javascript, и вовсе не потому, что эти языки имеют весомые преимущества. Скорее потому, что любой может осилив 5 строк введения начать лабать на этих языках сайты, пускай это спагетти-код, пускай там тоннами используются неинициализированные переменные, и т.д. Не требуется знать ничего: садись да пиши.
Что лаконичней?Вы выкусили маленький кусочек, сравните проект целиком.
Может, я чего не понимаю?Да, в питоне есть некотрые спортные моменты, но все особенности обоснованы.
Скорости питона как правило хватает для зачад для которых он расчитан.
Одно из главных преимуществ — проекты на питоне разрабатывать быстрее и проще поддерживать, чем на многих других языках.
Скорости питона как правило хватает для задач для которых он рассчитан.Для каких задач он подходит лучше всего?
Для задач, где основне время работы программы проходит в блокирвоках диска\БД\чего угодно.
Например взяли кадр с IP камеры, отдали в OpenCV, ждем результат, дождались и показываем, или взяли ввод полльзвоателя, отбработали, кладём в БД, ждем подтверждения, дождались и отвечаем.
Тот факт что pypy и подобные не стали настолько популярны, говроит о том, что базового питона для скорости хватает.
Понятное дело, что хочется «быть здоровым и богатым», но в реальности, обычно, приходится выбирать 2 из 3-х:
* Простой
* Быстрый
* Полезный
А в больших проектах, просто, используются разные языки и технологии вместе в зависимости от задач и возможностей.
Вот пример использования Apache Spark, на Python и Scala:Вау! Вот жто круто! Если использовать библиотеку, написанную и заточенную под Scala — то это, оказывается, удобнее всего делать на Scala.
Вы бы ещё сравнили вызов библиотеке на Pascal'е или что-нибудь подобное.
В чем тайный смысл двоеточия, если по идее, должно быть достаточно отступов для формирования блоков кода?
Если так посмотреть, то и in
не нужен. :)
Всегда удивляюсь, когда говорят о строгости и лаконичности питона.
Может, я чего не понимаю?
Рискну предположить что Вы не знаете с какими языками сравнивают, от того и удивляетесь.
Создатель Питона: я устал, я ухожу