Ошибки дизайна Python
Некоторое время назад, когда я изучал Javascript, я изучал Javascript: хорошие части, и мне особенно понравились главы о плохих и уродливых частях. Конечно, я не согласен со всем, поскольку суммирование дефектов дизайна языка программирования в определенной степени субъективно - хотя, например, я думаю, что все согласятся с тем, что ключевое слово with
было ошибкой в Javascript. Тем не менее, я считаю полезным читать такие обзоры: даже если кто-то не согласен, есть чему поучиться.
Есть ли запись в блоге или какая-нибудь книга, описывающая ошибки дизайна для Python? Например, я предполагаю, что некоторые люди посчитали бы отсутствие оптимизации хвостового вызова ошибкой; могут быть другие проблемы (или не проблемы), о которых стоит узнать.
Ответы
Ответ 1
Вы просили ссылку или другой источник, но на самом деле его нет. Информация распространяется во многих разных местах. Что действительно является ошибкой проектирования, и считаете ли вы только синтаксические и семантические проблемы в определении языка, или вы включаете прагматические вещи, такие как проблемы платформы и стандартной библиотеки и конкретные проблемы реализации? Вы могли бы сказать, что динамизм Python является ошибкой проектирования с точки зрения производительности, потому что это затрудняет создание простой эффективной реализации, а также затрудняет (я не сказал, что это абсолютно невозможно) создание IDE с дополнением кода, рефакторингом и другие приятные вещи. В то же время, вы можете поспорить за плюсы динамических языков.
Возможно, один из способов начать думать об этом - посмотреть на изменения языка с Python 2.x на 3.x. Некоторые люди, конечно, утверждают, что print
как функция неудобна, в то время как другие считают ее улучшением. В целом, изменений не так много, и большинство из них довольно маленькие и тонкие. Например, map()
и filter()
возвращают итераторы вместо списков, range()
ведет себя как xrange()
, а методы dict
такие как dict.keys()
возвращают представления вместо списков. Затем есть некоторые изменения, связанные с целыми числами, и одно из больших изменений - обработка двоичных/строковых данных. Это теперь текст и данные, а текст всегда Unicode. Есть несколько синтаксических изменений, но они больше касаются согласованности, чем обновления всего языка.
С этой точки зрения, кажется, что Python был довольно хорошо разработан на уровне языка (синтаксиса и семантики) по крайней мере с 2.x. Вы всегда можете поспорить о блочном синтаксисе на основе отступов, но мы все знаем, что это ни к чему не приведет... ;-)
Другой подход - посмотреть, какие альтернативные реализации Python пытаются решить. Большинство из них так или иначе связаны с производительностью, некоторые проблемы с платформой, а некоторые добавляют или вносят изменения в сам язык для более эффективного решения определенных задач. Разгрузившаяся ласточка хочет значительно ускорить Python за счет оптимизации этапов байт-компиляции и выполнения. Stackless добавляет функциональность для эффективных многопоточных приложений, добавляя конструкции, такие как микропотоки и тасклеты, каналы, позволяющие двунаправленную связь с тасклетами, планируя запуск тасклетов совместно или превентивно, и сериализацию, чтобы приостановить и возобновить выполнение тасклетов. Jython позволяет использовать Python на платформе Java и IronPython на платформе .Net. Cython - это диалект Python, который позволяет вызывать функции C и объявлять типы C, позволяя компилятору генерировать эффективный код C из кода Cython. Shed Skin добавляет неявную статическую типизацию в Python и генерирует C++ для автономных программ или модулей расширения. PyPy реализует Python в подмножестве Python и изменяет некоторые детали реализации, такие как добавление сборки мусора вместо подсчета ссылок. Цель состоит в том, чтобы позволить языку Python и разработке реализации стать более эффективными благодаря языку более высокого уровня. Py V8 соединяет Python и JavaScript с помощью движка V8 JavaScript - можно сказать, что это решает проблему с платформой. Psyco - это особый тип JIT, который динамически генерирует специальные версии работающего кода для данных, которые в настоящее время обрабатываются, что может дать ускорение для вашего кода Python без необходимости написания оптимизированных модулей Си.
Что-то из этого можно сказать о текущем состоянии Python, взглянув на PEP-3146, в котором описывается, как Unladen Swallow будет объединен с CPython. Этот PEP принят и, таким образом, является решением разработчиков Python о том, какое наиболее целесообразное направление выбрать в данный момент. Обратите внимание, что это касается производительности, а не языка как такового.
Поэтому на самом деле я бы сказал, что основные проблемы проектирования Python находятся в области производительности, но это в основном те же проблемы, с которыми сталкивается любой динамический язык, и семейство языков и реализаций Python пытается решить эти проблемы. Что касается прямых ошибок проектирования, подобных перечисленным в Javascript: хорошие части, я думаю, что значение "ошибки" должно быть более четко определено, но вы можете проверить следующие мысли и мнения:
Ответ 2
Есть ли запись в блоге или какая-то книга, описывающая ошибки дизайна для Python?
Да.
Он назвал список Py3K обратно-несовместимых изменений.
Начните здесь: http://docs.python.org/release/3.0.1/whatsnew/3.0.html
Прочтите все примечания к выпуску Python 3.x для получения дополнительной информации о ошибках в Python 2.
Ответ 3
Мой самый большой peeve с Python - и тот, который на самом деле не рассматривался при переходе на 3.x, - это отсутствие правильных соглашений об именах в стандартной библиотеке.
Почему, например, модуль datetime
содержит класс, называемый datetime
? (Не говоря уже о том, почему у нас есть отдельные модули datetime
и time
, но также класс datetime.time
!) Почему datetime.datetime
в нижнем регистре, но decimal.Decimal
- это верхний регистр? И, пожалуйста, скажите мне, почему у нас есть этот ужасный беспорядок в пространстве имен xml
: xml.sax
, но xml.etree.ElementTree
- что там происходит?
Ответ 4
Попробуйте эти ссылки:
http://c2.com/cgi/wiki?PythonLanguage
http://c2.com/cgi/wiki?PythonProblems
Ответ 5
То, что часто удивляет неопытных разработчиков, - это ошибки кандидата. Вот один аргумент по умолчанию:
http://www.deadlybloodyserious.com/2008/05/default-argument-blunders/
Ответ 6
Мой личный язык - это имя, обязательное для lambdas/local functions:
fns = []
for i in range(10):
fns.append(lambda: i)
for fn in fns:
print(fn()) # !!! always 9 - not what I'd naively expect
IMO, я бы предпочел искать имена, упомянутые в лямбде во время объявления. Я понимаю причины, почему он работает так, как он есть, но все же...
В настоящее время вы должны обходить его, привязывая i
к новому имени. Значение whos не изменяется, используя закрытие функции.
Ответ 7
Это скорее второстепенная проблема с языком, чем фундаментальная ошибка, но: переопределение свойств. Если вы переопределите свойство (используя геттеры и сеттеры), нет простого способа получение свойства родительского класса.
Ответ 8
Да, это странно, но я предполагаю, что вы получаете за переменные переменные.
Я думаю, причина в том, что "i" относится к коробке, которая имеет изменяемое значение, а цикл "for" изменит это значение с течением времени, поэтому при чтении значения поля позже вы получите единственное значение, которое осталось.
Я не знаю, как можно было бы исправить это, не сделав его функциональным языком программирования без изменяемых переменных (по крайней мере, без неконтролируемых изменяемых переменных).
Обходной путь, который я использую, заключается в создании новой переменной со значением по умолчанию (значения по умолчанию оцениваются в DEFINITION time в Python, что раздражает в другое время), что приводит к копированию значения в новое поле:
fns = []
for i in range(10):
fns.append(lambda j=i: j)
for fn in fns:
print(fn()) # works
Ответ 9
Я нахожу удивительным, что никто не упоминал блокировку глобального интерпретатора.
Ответ 10
Одна из вещей, которые я нахожу наиболее раздражающими в Python, - это использование writelines() и readlines() в файле. readlines() не только возвращает список строк, но также имеет \n символов в конце каждой строки, поэтому вы всегда должны делать что-то вроде этого, чтобы разделить их:
lines = [l.replace("\n", "").replace("\r", "") for l in f.readlines()]
И когда вы хотите использовать writelines() для записи строк в файл, вам нужно добавить \n в конце каждой строки в списке перед их написанием, например:
f.writelines([l + "\n" for l in lines])
writelines() и readlines() должны заботиться о концевых символах независимым от ОС образом, поэтому вам не придется иметь дело с ним самостоятельно.
Вы должны просто уйти:
lines = f.readlines()
и он должен возвращать список строк без символов \n или\r в конце строк.
Аналогично, вы должны просто уйти:
f.writelines(lines)
Чтобы записать список строк в файл, и он должен использовать предпочтительные символы командной строки операционной системы при записи файла, вам не нужно будет делать это самостоятельно в списке в первую очередь.
Ответ 11
Вы попросили симпатий; Я написал документ по этой теме некоторое время назад: http://segfaulthunter.github.com/articles/biggestsurprise/
Ответ 12
Моя самая большая неприязнь - range()
, потому что она не делает то, что вы ожидаете, например:
>>> for i in range(1,10): print i,
1 2 3 4 5 6 7 8 9
Наивный пользователь, пришедший с другого языка, ожидал, что и 10 будет напечатано.
Ответ 13
Я думаю, что в python много странных вещей, так как они обрабатывают встроенные/константы. Как показано ниже:
True = "hello"
False = "hello"
print True == False
Что печатает True
...
def sorted(x):
print "Haha, pwned"
sorted([4, 3, 2, 1])
Lolwut? sorted
- встроенная глобальная функция. Худшим примером на практике является list
, который люди склонны использовать в качестве удобного имени для локальной переменной и в конечном итоге сбрасывают глобальное встроенное.