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

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

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


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

Вы сами назвали их «особенностями». Их можно отнести как к киллер-фичам, так и к недостаткам.

Но выносить это в заголовок как «недостатки»? Как-то недохайпово.

Я, честно говоря, опираюсь на следующий текст из статьи:


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

Что вполне соответствует заголовку статьи

НЛО прилетело и опубликовало эту надпись здесь
Много ли вы делали проектов, где не хватало быстродействия именно Python кода?

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


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


Хотя в данном случае не понятно о чём статья, тут вроде нет открывающих покровов истин или деталей.

У меня на embedded-платформе воткнут labgrid (плагин для pytest). Так вот, набор тестов
(10 файлов по 50 строк) стартует 20 секунд. В два раза быстрее стартует вся остальная операционная система с systemd, веб-сервером и прочим-прочим.


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

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

Язык никогда не бывает виноватым, виноват рантайм. Ну и что с того?


Бенчмарки нищеброда

hell.py:


print("Hello world")

time python3 ./hell.py
--
Hello wordl

real    0m0.566s
user    0m0.432s
sys     0m0.042s

time luajit ./hell.py
---
Hello wordl

real    0m0.033s
user    0m0.008s
sys     0m0.021s

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

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


Похожее чувство было у меня когда узнал что Torch с Lua (проект развивался 15 лет) закрывают и одновременно facebook радостно продвигает «свой» PyTorch.
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
Я правильно понял, парсер логов, в котором IO занимает меньшую часть работы? В память что ли весь загружается? На порядок-другой, то бишь минимум в 10-100 раз быстрее. До смерти любопытственно взглянуть на питоновскую версию.
НЛО прилетело и опубликовало эту надпись здесь
В память что ли весь загружается?

попробуйте-ка на питончике сделать нормально mmap(). Намек понятен?

Намек понятен?
Неа. Не понял, какие выводы я из этого должен сделать.

mmap() позволяет очень сильно ускорить работу с файлами. Обратная сторона — это все требует отдельных функций для работы с такими файлами. Нативно в ЯП не получается это реализовать. Но вот NumPy в это умеет и это внушает определенную надежду, что на python можно писать относительно быстрый и относительно экономный с точки зрения памяти код.

mmap() позволяет очень сильно ускорить работу с файлами

Я всё еще не понял, как mmap должен ускорить работу в данном случае. Задача: прочитать целиком большой файл, ничего писать не надо. Я предположил загрузить файл в память целиком. Как в этой ситуации mmap ускорит IO или сэкономит память?

Фейспалм. Когда Вы читаете файл fread(), Вы имеете следующее:


  1. читаете неоптимальными блоками файл в ОЗУ с накопителя,
  2. дублируете данные — т.к. операционная система должна сначала считать файл в кэш, потом скопировать эти данные внутрь Вашего процесса.
    Эффективность в районе плинтуса. С помощью mmap() Вы полностью перекладываете чтение файла на операционную систему, дублирования данных не происходит.

Я предположил загрузить файл в память целиком

дополню — а как Вы будете считывать 16ГиГ файл при доступной ОЗУ всего 1 ГиГ? Будем ждать, пока условный fread() в памяти программы сожрет 16ГиБ и свалится в swap? Или реализовывать нетривиальные алгоритмы чтения определенного скользящего окна?


ОЧЕНЬ СТРАННО, что Вы не в курсе и даже не удосужились прочитать инфу по ссылкам

дополню — а как Вы будете считывать 16ГиГ файл при доступной ОЗУ всего 1 ГиГ?
Я вообще говорил о гипотетической ситуации загрузки файла в память целиком одним системным вызовом. Очевидно, не стоит этого делать, если у вас памяти 1 гиг, а файл 16 гигов.

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

Подскажите, я ответил на Ваш вопрос, Вы согласны с указанными доводами? Или требуется дальнейшее обсуждение?

Подскажите, я ответил на Ваш вопрос, Вы согласны с указанными доводами?
Я честно не понял, как mmap() поможет конкретно в ситуации, когда я загружаю файл целиком в память одним сисколом для чтения. Мне кажется, должно быть даже медленнее через mmap().

Поэтому будет здорово, если объясните.
Анализ нескольких временных рядов и их пересечений, например.
либо алгоритм несовершенный
Это скорее да, чем нет, но данные при этом принципиально ограничены. Усложнение алгоритма (добавление оптимизаций, кешей, неполные вычисления, мемоизация) — это время разработчика. При этом компилируемый ЯП может решить проблему без усложнений, оказавшись дешевле.
Жаль, ЯП, которые мне нравятся, неидеальны с GUI. Доминируют C++ и Python.
кешей… мемоизация

как минимум — трейд-офф большего потребления памяти, сложности на более быструю работу программы. Чудес не бывает. А потом — хопа, у нас память улетает, как в java.


это время разработчика.

все так.

Основные недостатки молодых динамично развивающихся ™ авторов:


  • желтые заголовки
  • "развенчание" старых "мифов"
  • создание новых мифов
Во-первых, это динамическая типизация

Данное словосочетание означает тот факт, что переменная, объявленная в этом языке, не имеет привязанного к ней типа.

Точно именно это означает?
Что же тогда выведет этот код для произвольной переменной?

type(foo)

Соглашусь, что в этом случае я написал не совсем чётко. Привязанный тип имеет не сама переменная, а объект, на который она указывает.


В Питоне вы можете делать следующее:


>>> foo = 1
>>> type(foo)
int

>>> foo = "foo"
>>> type(foo)
str

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


Это приводит к тому, что для того, чтобы гарантировать, что в конкретном месте программы переменная foo имеет тип baz, то приходится писать бесконечные:


isinstance(foo, baz)

или


type(foo) is baz

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

тип данных, хранящихся в переменной
type(foo)

Возвращает тип объекта, ссылка на который записана в переменной. При этом у самой переменной типа нет — она может хранить ссылку на объект совершенно любого типа.


PEP484 ввел Type hints, которые указывают, что в конкретной переменной ожидаются объекты конкретного типа, но type hints не влияют на то, что происходит в рантайме. Интерпретатор практически полностью их игнорирует. И появились они в языке только в угоду тому, чтобы можно было сделать линтеры вроде mypy, которые их проверяют. Но при этом нет гарантий, что в переменную с type hint будет записан объект тип которого соответствует type hint.

Очень дельный комментарий про PEP484, про который я совсем забыл упомянуть в статье. Спасибо!


Здесь также можно почитать о том, как применять аннотации типов в Питоне.

Мне кажется, желание везде навтыкать проверку компилятором соответствиятипов переменных, это просто модное увлечение. ИМХО, нужно объявить возможность динамически менять тип в рантайме клиллер фичей и оставить эту тему в покое. А то уберем динамическую типизацию, сделаем так, чтобы компилятор ругался на типы, и придется писать в пять раз больше кода и использовать зубодробительные паттерны, чтобы обойти это «удобство».
Полно языков со статической типизацией. Вот пусть они этим и занимаются. Питон не про это.
НЛО прилетело и опубликовало эту надпись здесь
это просто модное увлечение
Просто проекты разрастаются, начинаются проблемы с соблюдением контрактов и попытки их решить. Это не только языков программирования касается. Все крупные динамические языки уже получили возможность таких проверок — это не мода, а системный запрос индустриальной разработки. То же можно сказать про XSD+ для XML, развивающийся OpenAPI и т.п. Мне кажется, карьера разработчика, где не приходит понимание, почему так в мире происходит (ссылка на моду — это форма отрицания), идёт не очень хорошо.

Добавлю к статье, что по-моему Python страдает от своей безграничной возможности monkey patching и святой уверенности многих разработчиков в наличии некоего особенного pythonic пути, решающую пластилиновые проблемы (в этом они расходятся с аналогично пластилиновым, но неидеологичным Javascript'ом).
НЛО прилетело и опубликовало эту надпись здесь
Идеального языка программирования не существует. И Python тоже не идеален. Но с формулировкой автора не согласен. Это не недостатки языка, а его особенности. И эти особенности просто нужно учитывать при выборе языка программирования для решения конкретной задачи.

У автомобиля есть два недостатка — он не летает и не плавает.

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

..., а то бы зафигачил всё в одну строку и горя бы не знал.

НЛО прилетело и опубликовало эту надпись здесь
И опубликовал в хабе «Ненормальное программирование».
А что не так? В C++ всё-же удобней — перемешал блоки кода как надо, один if вынес из цикла, два переместил в цикл, скопипастил пару строк из другой функции, нажал Ctrl-s и clang-format автомагически всё выровнял красиво. А с Питоном такой трюк не пройдёт — сиди, выравнивай отступы руками.

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

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

Java смотрит на это с недоумением.

Помимо специфического архитектурного решения под названием GIL
будете громко смеяться, но нет, это «явление» не является чем-то специфичным именно для Python, это зачастую незаменимый механизм обеспечения консистентности поведения интерпретаторов, иначе его бы назвали GPIL, Global Python Interpreter Lock или GPL, Global Python Lock
Кстати, используя multiprocessing можно вполне жить и с GIL.

Угу, угу


В питоне удобно сделали, совместили параллелизм зелёных потоков и накладные расходы нативных тредов.
НЛО прилетело и опубликовало эту надпись здесь
Вообще, сколько от Зена осталось правды?

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


Например, как в Python можно сделать singleton? С помощью декоратора, с помощью метакласса, с помощью объекта класса, с помощью обычного модуля — любой объект модуля есть singleton. И как же в Python правильно сделать singleton? Да как угодно. Только не начинайте, пожалуйста, что singleton сам по себе — это неправильно. :)


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

Сразу приведу железный аргумент (по крайней мере в контексте Python): если бы singleton был чем-то неправильным, объект None не был бы синглтоном, так же как и его аналоги в других языках (в основном NULL/Null/null/NIL/nil). Думаю, можно без преувеличения сказать, что этот синглтон лежит в основе Python, как один из фундаментальных кирпичиков (возможно, схожим образом ситуация обстоит и в некоторых других языках программирования). Например, любая процедура на самом деле является функцией, неявно возвращающей None
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Все динамически типизированные языки страдают от быстродействия по сравнению статически типизированными (компилируемыми). И в интерпретаторе нету доступа к branch-prediction и всяким другим плюшкам. Но есть же примеры где на динамической типизации применялись оптимизаторы с целью ускорить выполнение кода. Javascript это интерпретируемый язык, также как и Python. Но есть V8 который делает рантайм оптимизации на основе уже выполненных кусков кода. Да, JS компилируется в машинный а не байткод, но это не исключает подхода с двумя компиляторами, основным и оптимизирующим. Почему такой подход не может быть реализован для Python?

Судя по интернету, это уже есть (PyPy meta-JIT). Философия python, как языка "с батарейками" играет злую шутку, когда просто слишком много чего надо тянуть

НЛО прилетело и опубликовало эту надпись здесь
не думаю, что это произойдёт раньше, чем будет стёрта граница между оперативной и постоянной памятью (очень не скоро)
НЛО прилетело и опубликовало эту надпись здесь

Хорошо. Давайте гипотетический пример. Предположим, я хочу писать быстро и одинаково неэффективно под разные архитектуры. Ваши предложения? Код на пайтоне — дешев, сердит и переносим.

Есть ещё один недостаток. Высокая сложность. У меня нет никакого желания его учить: сложный, медленный, бесят отступы(у меня другие правила оформления структурности кода, одинаковые для ВСЕХ ЯП которыми пользуюсь), не удобный для пользователя(надо устанавливать дополнительный софт, причем разный для разных версий). Не каких плюсов у этого ЯП в упор не вижу…

Попробуйте тогда c++

Я использую высокоуровневый ассемблер в основном(UASM), мне на нём проще всего работать. А плюсы реже использую. А питон… ну да, ну да, миллионы мух не могут ошибаться (с).
«Нет никакого желания учить язык» != «Высокая сложность»
В Питоне переменная, будучи объявленной, не обязана прекращать существование после выхода из родного блока отступов.

Самый стрёмный из недостатков! :(
Самый стрёмный из недостатков! :(
Вас обманули, это не недостаток. Иначе бы не работал вот такой код:
if a:
    b = 1
else:
    b = 0
print(b)

Либо автор сам не понимает, о чем пишет, либо выражаться надо яснее.

Да хоть в пхп попробуйте, там так же работает:
if ($a == 1){
    $b = 1;
} else {
    $b = 0;
}
print($b);

Человек после C/C++/Pascal/других языков привыкает к явному объявлению начала жизни переменной внутри блока, в котором она объявлена. А мне, к примеру, после lua, где любая нелокальная переменная отправляется в _G (глобальную таблицу), писать на питоне — значит бороться с рефлекторной перестраховкой "сначала объяви local, только потом используй"

А ещё в других языках переменная b объявленная в двух разных блоках, может иметь два разных значения (и два разных типа).
Вы уверены, что это:
1. недостаток
2. питона
?

Человек после C/C++/Pascal/других языков привыкает к явному объявлению начала жизни переменной внутри блока, в котором она объявлена.
Ну поздравляю, вы сейчас говорите про статическую типизацию. Только причем тут питон? У него динамическая.

писать на питоне — значит бороться с рефлекторной перестраховкой «сначала объяви local, только потом используй»
Опять таки, вы уверены, что это «недостаток питона», а не особенность динамической типизации? Я пример с пхп зачем приводил?

А еще, вы уверены, что вы не путаете блок отступов и область видимости? Потому что это разные вещи. У функций, например, область видимости своя, а у циклов или условий — нет.

Так и скажите, я не люблю динамическую типизацию. А то получается, что лыжи плохие, потому что не едут по асфальту.

Я не говорил про недостаток, я говорил про неудобство.


Я пример с пхп зачем приводил?

Честное слово, лучше бы не приводили. У меня от PHP ни одного приятного впечатления никогда и не было.


Опять таки, вы уверены, что это «недостаток питона», а не особенность динамической типизации? Я пример с пхп зачем приводил?

При чем тут вообще типизация, речь про область видимости объекта. После С/С++/js/добавьте по вкусу ожидаешь, что синтаксический блок создает новую область видимости, а в python это не так. Когда за один день работаешь с четырьмя языками сразу — легко забыться и начать использовать приемы одного языка в другом.

Честное слово, лучше бы не приводили. У меня от PHP ни одного приятного впечатления никогда и не было.
Речь не о том, нравится или не нравится, а о том, что дело не в питоне, а в типизации (не прямо, но косвенно). О чем я и толкую.

При чем тут вообще типизация, речь про область видимости объекта. После С/С++/js/добавьте по вкусу ожидаешь, что синтаксический блок создает новую область видимости, а в python это не так.
Окей, этот же код привожу на JS, переменная при выходе из блока не уничтожается:

if (a==1){
	b = 1;
} else {
	b = 2;
}
document.write(b);

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

Еще один пример JS (такой же пример используется в статье, но на питоне):
for (var i = 0; i < 9; i++) {
   document.write(i);
}
document.write(i);

Смотрите ка! Тут тоже переменная i не уничтожается при выходе из блока. Python, PHP, JS (все с динамической типизацией) — везде одинаково работает. Как мне еще это написать, чтоб вы поняли наконец, что блок кода и область видимости — разные вещи, и что питон тут не причем?

Этот же код НЕ будет работать на C++:
for (int i = 0; i < 9; i++ ){
    cout << i;
}
cout << i;

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

Еще один пример JS (такой же пример используется в статье, но на питоне):

Именно поэтому в js ввели ключевое слово let

Мне нравится как сделано в Rust:


let b = if ... {
    // ...
    1
}
else {
    // ...
    2
};

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


Также скоупы тоже могут возвращать результат:


let a = {
    // ...
    res
};
Потому что if — это выражение, и оно может возвращать результат

25 лет назад в Ruby было точно так же, вообще-то.

НЛО прилетело и опубликовало эту надпись здесь
Вас обманули, это не недостаток. Иначе бы не работал вот такой код:

Потому что это должно выглядеть вот так:


b = 1 if a else b

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

b = 1 if a else b

Как-то мне не очень нравится.

Мне тоже не нравится. Я бы предпочёл писать


b = if a:
        1
    else:
        0

, но это не вписывается в синтаксис Python.

Да на самом деле нет, потому что, первый пример требует, чтобы a умело конвертироваться в bool, а второй — чтобы a умело конвертироваться в int, и эти реализации необязательно согласованы друг с другом. Ну и потом, это просто пример, вполне можно представить случай, когда в ветках then и else более сложные выражения.

Ну и в общем случае, для более-менее сложной логики, я предпочитаю что-то вроде:
logic = { <rule_1>: <case_1>,
<rule_2>: <case_2>
.....
a = rule_<N>}
logic[a]

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

Почему терять ленивость? Если нужны сложные кейсы оборачиваем их или в лямбды в простейших случаях, либо в функции или методы.
Питон именно про это, использование питоновских типов для максимальной выразительности.
Если сделать так как предлагают, это будет провоцировать создание зубодробительных условных конструкций, которые нужно час разбирать с бумажкой, чтобы что-то понять. Это на мой взгляд худшее, что может быть.
Добавил: причем в большинстве случаев функции гораздо лучше чем лямбды:
— говорящее имя.
— Расшивается сложность.
Лямбды неудобочитаемы, как и все однострочники.
А так? ))

b = a and 1 or 0

Потому что это должно выглядеть вот так

Мы тут область видимости блоков обсуждаем, а не идиоматичность кода. Вы бы еще сказали, что переменные не должны называться a и b, а должны иметь осмысленное имя.

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

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

Ну, во-первых, он медленный.

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

Не все пишут код для серверов на Intel Xeon. А вот использовать быдлокод, написанный на питхоне — вынуждены все. Увы

В принципе, медленных языков больше не существует. Все упирается в IO.

Лол, нет. Иначе бы тот же numpy писали на Python, а не на C и Fortran.

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

Публикации

Истории