5 вещей, которые мне не нравятся в Python
Макс ИщенкоОпубликовано 25.05.2007 в Статьи
Иван Сагалаев написал про 7 вещей, которые я не люблю в Django. Я предлагаю эту тему развить и написать каждому о тех вещах, которые вам не нравятся в используемых инструментах программирования.
Итак, 5 вещей, которые мне не нравятся в Python:
- не-юникодные строки. Дихотомия str/unicode принесла всем разработчикам не-ascii приложений кучу головной боли. Остается ждать Python 3.0, где обещаны только unicode строки и отдельный тип byte для несимвольных данных (да-да, в Java так было с самого начала).
- инструментальные средства. Вспомнив о Java, сразу вспоминаются мощные IDE типа Eclipse, реально ускоряющие разработку. Лучшая IDE для разработки на Python это, ИМХО, vim + ctags + grep. Связка не плохая, нет, но хотелось бы большего, чего-то из 21 века, а не из конца 70-х.
- семантические пробелы (significant whitespace). К этому, конечно, привыкаешь, но все равно, я бы предпочел обычные пробелы, не несущие смысловой нагрузки. Не говоря о том, что в итоге мы остались с lambda-выражениями вместо полноценных анонимных функций. Плюс, массу времени можно было бы сэкономить на flame wars.
- качество stdlib. Не все модули, входящие в Python standard library, одинаково хороши. Многие были включены чересчур поспешно и непродумано. Фиксация в stdlib означает как правило фиксацию API (иногда кривого) и невозможность избавиться от модуля в будущем.
- GIL и работа на multicore окружении. Не очень ясно, насколько хорошо (или плохо) будет вести себя Python с повсеместным распространением multicode машин.
Пояснение: речь идет о критике инструмента, который я выбирал сам и использую совершенно сознательно. Любой, самый совершенный инструмент, неидеален. А некоторые даже утверждают, что если вы не видите недостатков значит просто не освоили его достаточно глубоко.
Понравилась статья? Подпишись на обновления по RSS/E-mail

(1 голосов, средний: 4 из 5)
По поводу GIL Гвидо, насколько я понимаю, предлагает всем избавиться от необходимости делать многопроцессность через треды. Пишет, что треды популярны исключительно потому что их пропогандирует Java-сообщество и Windows-сообщество, где это действительно единственное нормальное средство. Но вот в юниксах он предлагает пользоваться процессами вместо тредов, потому что это проще.
Вот, откопал ссылочку: http://mail.python.org/pipermail/python-3000/2007-May/007414.html
Иван: ссылку читал и не доверять Гвидо особых оснований нет. Но “осадок остался”.
С этим пунктом не согласен.
також згодний зі всім крім “significant whitespace”, як на мене це невелика плата за більш читаємий код. А чому хочеться саме анонімності функцій? В пітоні і звичайну написати не складніше. ( я серйозно, самому цікаво ).
ну і api stdlib зафіксований не намертво, нечасто але міняють. надіюсь на кращий в python 3000.
Очень согласен с проблемой отсутствия инструментария – реально очень хотелось бы действительно удобный IDE. Пока пользуюсь Intype – но это не полноценная IDE, как еклипс или студия.
Важным недостатком считаю также малое количество хостеров поддерживающих возможность использования этого фреймворка.
А чем эклипс + pydev неустраивает в качестве иде?
для екліпса є pydev, ну і крім нього ряд відкритих і комерційних IDE ( Wings, BlackAdder, Eric, python режим в slickedit але він не настільки розвинутий як для С++ )
Отступы в питоне – это его плюс. В целом претензий к язку не имею вообще.
Я пробовал практически все – не подошли по-разным причинам…
Как-нибудь напишу статью что к чему
вместо пробелов меня достает скорость питона. точнее тот факт, что на нем писать многое из того, что приходится писать на Сях.
Сорри — слово пропустил
вместо пробелов меня достает скорость питона. точнее тот факт, что на нем нельзя писать многое из того, что приходится писать на Сях.
bialix
можно пример того что нельзя писать?
Например, ray tracing. Написать можно, а толку?
Ну если в академических целях то можно наверное.
Потом оформить критический по скорости функционал в отдельный модуль, и переписать на C
На Mac’е для питона хорош TextMate. Он вообще для всего хорош
По поводу IDE это ты загнул: посмотри на eric и удивись!
http://en.wikipedia.org/wiki/Eric_Python_IDE
http://www.die-offenbachs.de/detlev/eric.html
И снова про IDE. Попробуй WingIDE. Платное, правда. Как-нить разживусь деньгами и может куплю. Но пока что лучшее, что видел для питона. Пробовал много всякого, кроме Eclipse+pydev – говорят тормозит порядочно, да и зависимостей слишком много emerge-ить
Самая лучшая штука в это IDE, это её дебаг(брякпоинты, Step out/in/over). Недостаток – пока что однотредовый. Но в версии 3.0, которая ща в альфе, обещают multithread. Так же обещают автодополнение в debug-консоли. Это облегчит жизнь.
Кстати, об автодополнении. В Wing оно просто великолепное.
Вобщем, must have, как говорится.
Мои претензии:
Создание переменной просто по присвоению. Вроде же практически все интерпретируемые языки прошли это и пришли к необходимости явного декларирования переменных, а тут такая деская болезнь. Это же касается создания атрибутов объекта по присвоению, хотя и не в такой мере: его в принципе можно решить частично решить через __getatts___/__setattr .
Обращение к методу без () дает просто ссылку, которая в логинческом представлении дает true. Само по себе было бы терпимо если бы не каша даже в стандартных библиотеках: почему у файлового дескриптора isatty это метод, а closed это атрибут?
Вроде еще что-то было, но уже забыл
Отступы в python это большой плюс, на мой взгляд.
А как среду разработки можно попробовать Komodo.
str+unicode – почти не проблема:
import sys
reload(sys)
sys.setdefaultencoding(’Cp1251′)
После этого юникодные и обычные строки прозрачно преобразуются.
Только вот почему хак с релоадом обязателен, никто мне не обьяснит?
Ещё пара-тройка вещей:
отсутствие статической типизации (пример, как сделано грамотно – boo); хотя, по-моему, это неспешно так рассматривается для python 3.0;
отсутствие объявления локальных переменных, типа my в перле; вообще, не хватает поддержки статического анализа текста программы;
отсутствие общего namespace для стандартных модулей, типа
import std.re
from std import cgi
А отсутствие унарного оператора инкремента/декремента?
А что же таки мешает выбрать язык/технологию по задаче/вкусу? Нравится питон – пользуй питон. Хочешь статики и модных иде – возьми жабу с нетбинсом. Со тредами – туда же
И с удобными гуями. Нужна скорость – сиплюсы юзай. Полностью, или только для модуля – согласно Ответственному Решению.
Бывает, правда, заказчик диктует, на чем надо писать… Но даже тут можно найти компромис
Навеяло нелюбовью к пробелам, созданию переменных по присвоению, тредами и жаждой к прочим “вкусностям” из соседних технологий/языков.
многое из перечисленного на самом деле не проблемы питона. т.е. большую часть можно либо принять как фичу, либо списать как нечто, не относящееся к языку как к таковому. например, пресловутые пробелы это однозначно фича – без них питон не питон. отсутствие статической типизации – вещь полезная, но опять же, язык задумывался без нее, так что можно смириться. IDE, скорость, GIL – это все особенности окружения или конкретной реализации языка (пусть даже единственно кошерной реализации). аналогично, operator++() не меняет язык принципиально – ну будет он чуть ближе к С, и что?
А вот что действительно раздражает – так это когда фича есть, и вроде полезная, но сделана настолько криво, что каждый раз морщишься, когда ее используешь. те же однострочные лямбды, или эти подчеркивания и self этот идиотский. и что интересно, все привыкают, и пользуются, и ходят, как клиенты от Levine the Genius Language Designer. впрочем, справедливости ради должен заметить, что в других подобных языках (ruby, perl) таких горбов куда больше, так что Pythonic Way это все-таки скорее хорошо, чем плохо, как мне кажется.
о да. кривость питоновского пространства имен и поиска в нем это конечно горбуха номер раз в моем списке.
впрочем, поищите в инете python warts – умные люди без меня давно список составили.
А я в Python терпеть не могу __совершенно_омерзительные_конвеншны__ с использованием знака подчеркивания, отсутствие нормальной инкапсуляции и необходимость указывать self в качестве первого параметра любого метода.
объяснюсь: речь идет о критике инструмента, который я выбирал сам и использую совершенно сознательно. Любой, самый совершенный инструмент, неидеален. А некоторые даже утверждают, что если вы не видите недостатков значит просто не освоили его достаточно глубоко.
мне вот не нравитcя отсутсвие полиморфизма как аткового и невозможность операции присвоения в условиях
Спасибо, посмиялси =)
* нет iif
http://lambda-the-ultimate.org/node/1402#comment-16034
* нет встроенной поддержки lazy evaluation выражений
* dict.setdefault: аргумент default должен быть lazy evaluated
> терпеть не могу __совершенно_омерзительные_конвеншны__ с использованием знака подчеркивания
Варианты? Вполне нормальная штука, имхо. Никак не мешает и не напрягает.
> необходимость указывать self в качестве первого параметра любого метода.
Это как раз плюс. Или надо иметь какие-то два магических типа функций? Одни – которые функции, вторые – которые методы? А у меня вот сейчас есть функция, которая одновременно служит в некоторых местах функцией, а в некоторых классах – методом. Что ты предлагаешь взамен?
Не так давно в комментах про обсуждение ФЯ некто указал, что мои вопросы про словари в Хаскеле сводятся к тривиальному “как писать на Хаскелле питон-программы”. Здесь аналогичный случай, только наоборот.
Однако, проблема эта есть. Питон недостаточно быстрый язык, и всякие ленивые штучки — это реальный ускоритель. Только они отсутствуют в ядре языка, и это не есть гуд. Приходится изобретать всякие костыли.
Хотя, иногда костыли получаются очень даже ничего. Например, реализация ленивого импорта модулей: чтобы не импортировать сразу все, а только по мере надобности, это очень интересная вещь. Реализована (наверное первыми) в исходниках Mercurial, под названием demandload. Затем эту идею Джон Майнел из команды Bazaar перенес ее в базу кода bzr и попутно внес некоторые правильные изменения (модуль lazy_import). Так что (кому интересно) — можно и поглядеть.
bialix, а для чего собственно Python недостаточно быстр? (Т.е. где же это lazy evaluation спасает ситуацию?)
Если говорить без конкретики — когда у вас есть много разнородных данных, но для выполнения каких-то операций не нужно обрабатывать их все сразу.
Например, у вас есть база каких-то однотипных элементов информации, при этом каждый элемент хранит много кусочков информации о разных аспектах. Допустим какждый аспект нужно как-то предобработать (превратить в некий питон-объект) для последующей массовой обработки всей базы. Если вы не знаете заранее, какие именно аспекты будут учавствовать в обработке, то удобно не делать предобработку всей имеющейся информации, а оставить эту предобработку как ленивую операцию, которая будет выполнена перед тем как данные будут реально востребованы.
Касательно lazy import. Как все знают при первом импорте модуль не только компилируется в байт-код, но и исполняется. Если в вашей программе куча импортируемых модулей, которые не обязательно испольуются все сразу, а некоторые операции вообще нуждаются в минимальном наборе импортируемых данных — вы получите большой оверхед при старте приложения. Чтобы уменьшить этот оверхед целесообразно использовать ленивый импорт.
Все сказанное выше относится к тем приложениям, которые предназначены к выполнению множества разнообразных операций над единым набором входных данных. Для приложений построенных по принципу — делать хорошо только одну вещь, такая ленивость может и не дать никакого эффекта.
Всем любителям lazy_somethig посвящается
))
1) lazy_import – идея неплохая но, к сожалению, __очень__ слабо применимая,
вещи при импорте, которые должны
причем ее обязательно нужно делать “вручную”, автоматически питон это не сделает.
Причины следующие:
а)Зачастую импорт идет не import something, а from something import x,y,z
как легко понять никакой lazy в таком случае не поможет.
б)Некоторые модули делают разные умные(или не очень
быть сделаны именно в момент импорта. Например модифицируют
стек обработчиков urllib2, или добавляют onexit функции, устанавливают
перехватчики исключений,etc.
Отдельный прикол такое -
…..
try:
if dt == nnn:
import XXX
except:
sys.stderr.write("Can't load appropriate plugin for nnn exit.")
sys.exit(1)
В этом случае весь lazy выйдет полным боком т.к. для того что-бы определить
что утилита не подходит для данного случая придется ее слегка поисполнять
и только потом Вы узнаете что она не подошла.
в)Автоматическим lazy_import невозможно такое обработать
(приведен упрощенный пример, все может быть гораздо хуже)
try:
import XXX
except ImportError:
try:
import YYY as XXX
except ImportError:
XXX = Default
Иначе говоря если модуль импортируется – он должен быть исполнен, поскольку
компилятор не способен определить имеется ли внутри модуля зависимость от
порядка загрузки и т.п.
2) lazy_evaluated данные.
Тут все сложнее, если сразу – то они автоматиски совсем никак не возможны,
а неавтоматически Вы можете сделать их сами, только толк от них будет в
крайне редких случаях.
Код примерно такой:
class Lazy(object):
def __init__(self,f,*dt,**mp):
self._func_ = [f,dt,mp]
def __getattr__(self,name):
try:
return getattr(self.__dict__['_x_'],name)
except KeyError:
self._x_ = self._func_[0](*(self._func_[1]),**(self._func_[2]))
return getattr(self.__dict__['_x_'],name)
c = Lazy(lambda : [])
print c.append
print c.append(1)
Код очень примерный, для толковой работы нужно перегрузить
почти все остальные специальные методы в классе X и все равно
грабли останутся. К тому-же видно что на каждое обращение
к аттрибуту объекта есть весьма основательные накладные расходы.
Есть еще вариант модификации вверхлежашего локального скопа
с заменой там себя, примерно так:
import sys
class Lazy(object):
def __init__(self,f,*dt,**mp):
self._func_ = [f,dt,mp]
def __getattr__(self,name):
if '_x_' not in self.__dict__:
self._x_ = self._func_[0](*(self._func_[1]),**(self._func_[2]))
_lk = sys._getframe(1).f_locals
keys = list(_lk.keys())
for i in keys:
if self is _lk[i]:
break
_lk[i] = self._x_
return getattr(self.__dict__['_x_'],name)
#Это тоже нужно основательно дописывать, но идея видна
c = Lazy(lambda : [])
print c
print c.append
print c
print c.append(1)
Это гораздо быстрее, но имеет одну важную проблему(см. ниже).
Но в обоих случаях остаются следующие грабли:
1)Невозможно перехватить обращение к специальным аттрибутам
__dict__,__slots__,__str__,etc. А это широко используется
т.н. pythonic кодом.
2)Такие объекты будут очень плохо дружить с C-кодом,
точнее первый вариант кое-как, а второй вообще никак,
т.к. он промодифицирует питоновский локальный скоп,
что на C объекте никак не отразится.
3)Основная проблема – невозможность перехватить операции
and,or,not,is + ф-ции type,isinstance,etc.
Отдельный прикол – оператор =. Можно написать
x = my_lazy_object
y = my_lazy_object
…..
Это все – дополнительные минусы в копилку второго варианта,
т.к. каждый их x,y,.. будет лезть в скоп наверх и там себя
по разу тыкать.
Других способов сейчас AFAIK быть не может.
По поводу изменения компилятора с целью поддержки lazy_objects.
Это так-же имеет вагон проблем, сходных с предыдущими + еще вагончик(
очень долго все описывать – не буду здесь) + две полные
“бяки”, а именно как бы Вы не внесли lazy в интерпретатор Вы замедлите
работу с non-lazy объектами из-за необходимости из С-кода
проверять “А не с lazy ли это мы сейчас работаем”. Плюс куча проблем
по отслеживанию, где же именно lazy нужно расчитать и подставить.
Не стоит сравнивать питон с лиспом – он __совсем__ другой и то
что лиспу хорошо потону надо еще переварить.
И на последок – всем советчикам по кардинальному улучшению питона.
).
Оформите свои предложения на английском и запостите в python-dev
рассылку, а мы посмеемся читая комментарии к этому посту
Или просто последите за python-dev с полгода и мысли типа
“а я вот тут такую клевую штуку придумал – вот бы ее в питон засунуть,
а то они там дураки сами-то еще за 15 лет не дошли”
будут основательно фильтроваться минимум недельным обдумыванием.
Блин, пробелы в коде съелись (((. Но, имхо, для истинных питонеров,
их восстановить не проблема. )
А все таки GIL. Если запускать отдельный питоновский процесс, то сколько памяти съест каждый такой процесс?
Товарищ koder привел мощную критику lazy_something, однако он глубко не вникал, поэтому она хоть и кажется правдоподобной. но таковой не всегда является.
Нет, не сделает. В самом питоне оно не встроено, поэтому не сделает. В проекте Базар ленивый импорт делается так:
from brzlib import lazy_importlazy_import.lazy_import("""
from bzrlib import error, osutils
""")
Как видно из этого примера, добавляются 3 строчки-обвертки, а внутри используется обычный импорт-синтаксис питона.
Как показано в примере выше — помогает. Замечание мимо кассы.
Пример неудачный. Чем ленивый импорт в этом случае будет отличаться от импорта скажем внутри какой-то функции?
def foo(arg):import spam
return spam.eggs(arg)
Делать sys.exit(1) только потому, что что-то не импортируется — это плохой стиль. Замечание опять же мимо кассы, потому что ленивый импорт следует применять тогда, когда он нужен, а не где попало.
Ленивый импорт предназнаен для другого. Ловить ImportError — это когда вы не знаете загрузится какая-то внешняя либа или нет. В проекте Базар лениво импортируются только внутренние модули, отстствие которых означает, что у вас поломанная инсталляция или ваш диск начал сдыхать.
Удивительно, и для чего же в Питоне duck-typing??? Подменить в ран-тайме даже один класс другим можно, а вы вот так, с места в карьер. В вашем примере не хватает одной малости. Lazy данные так всегда и остаются lazy. И все время будет срабатывать __getattr__. Lazy должен быть временным промежуточным объектом, который подменяется на реальный при первом обращении. Например, смотрите в исходниках Базар модуль lazy_regexp, который позволяет откладывать компиляцию регулярного выражения до первого использования.
Все остальное изложено вами верно, у ленивых вещей в питоне много ограничений, поскольку они реализуются искусственно, поэтому можно найти еще полмилиона причин почему они не будут работать в вашем коде. В коде Базар они работают и дают реальный выигрыш. Все остальное — спор о вкусе ананасов.
Да, это ценное предложение. Учитывая, что патчи для исправления ошибок в самом питоне (не добавление новой функциональности!) лежат годами на sf.net, а теперь в новом раундап трекере, то, да, кардинально улучшать питон бессмысленная затея. В проекте Базар никтоне ставит перед собой такую задачу. Нам нужно, чтобы Базар работал как задумано. Поэтому мы сами переписываем неустраивающие части питона под себя. Никто в python-dev никакие депеши не шлет.