NB: многа букаф, потому вступление можно опустить и перейти сразу к булетпоинтам
сразу оговорюсь, у меня довольно специфический взгляд на питон. Я не программист, точнее, не совсем программист: я статистически обрабатываю (исследую, сторю модели) данные с некоторых real-time систем. Данных очень много, терабайты, потому "руками" делать ничего не получится. Основной мой рабочий инструмент — matlab, который является практически идеальным инструментом, когда речь идет о работе с числовыми данными. Проблема в том, что помимо собственно регрессий приходится работать с окружением, как то: закачивать данные из базы, парсить логи итд. К сожалению матлаб устроен так, что он катастрофически теряет в производительности как только речь начинает идти о структуре данных сложнее чем матрица. Потому, когда пришлось столкнуться с питоном (у нас на работе это стандартный scripting language), то у меня поначалу было большое воодушевление: все просто, лекго и красиво. Потом однако стали видны недостатки.
Вопрос мой к гуру состоит в том: эти недостатки реальные, или же это я пока просто недостаточно pythonic, и со временем наоборот, начну считать эти черты достоинствами?
1. отладка. когда в матлабовской программе происходит критическая ошибка, то матлаб вываливается и дает мне интерпретатор в месте, где ошибка произошла. Это означает, что я могу посмотреть значения всех переменных, построить графики, сохранить что в файл, попробовать сделать что-то по-другому. Это на порядок облегчает отладку. В питоне ничего подобного я не видел. Т.е. если ошибка происходит, то интерпретатор дается в "module scope" а не непосредственно после той строчки, где была ошибка. Конечно, это зависит от IDE, но ни в одной из тех, о которых я читал, я не видел такой заявленной возможности.
2. видимость переменных. Если переменная определена в "module scope", то все вызываемые функции ее видят! Это отличается от того, как ведут себя другие языки. Питон во многом ведет себя непривычно, но где-то это удобно и обосновано (присваивание например). Преимущества же таких правил видимости непонятны. Пару раз у меня это приводило к неприятным ошибкам.
3. невозможность создать обычную plain структуру данных. Это раздражает. Приходится заводить пустой класс, создавать его и присваивать ему атрибуты (так советуют в официальном python tutorial). Т.е. потом, когда придется читать код, вместо чтения просто объявления структуры, придется рыскать по коду и смотреть, что же там присваивается.
5. Интерфейсы. Это действительно полезная фича из с++/java
Для того чтобы в питоне мне сделать интерфейс я создаю такой класс
class IForecastingStrategy:
def forecast(data):
raise NotImplementedError
и наследую от него. Но в таком случае у меня нет никакого способа сказать, что, скажем в функцию должен передаваться класс, наследующий от этого интерфейса.
6. очень плохая производительность при работе с матрицами (линейная алгебра. я использую numpy.linalg). Ну тут непонятно до какой степени виноват питон, а до какой степени lapack/blas которые идут с ним в комплекте. (матлаб поставляется с MKL, которая является самой быстрой на intel, но к сожалению она закрыта)
вот как то так, сорри если немного сумбурно
18.01.09 10:48: Перенесено модератором из 'Декларативное программирование' — Odi$$ey
А>1. отладка. когда в матлабовской программе происходит критическая ошибка, то матлаб вываливается и дает мне интерпретатор в месте, где ошибка произошла. Это означает, что я могу посмотреть значения всех переменных, построить графики, сохранить что в файл, попробовать сделать что-то по-другому. Это на порядок облегчает отладку. В питоне ничего подобного я не видел.
Я никогда не пользовался IDE и отладчиком для питона, но знаю, что подобные вещи реализуются посредством eval. Т.е. вы можете перехватить исключение, в некоем месте его обработать, вызывая eval для введенной из диалого или еще откуда-то строки, и в этой строке можете делать все, что угодно — смотреть переменные, писат в файл и т.п. На таком принципе работают, например, встроенные отладчики веб-фреймворков. Так же, попробуйте использовать ipython для REPL, и, возможно, отладки в этом стиле.
А>2. видимость переменных. Если переменная определена в "module scope", то все вызываемые функции ее видят!
Естественно, она же в той же области видимости, что и они. Это более логичное поведение, чем, скажем, в си, где функции, определенные ниже переменной, ее видят, а ниже — нет. Так как модуль является структурной единицей программы, а номер строки — нет. Если подобное поведение вызывает у вас проблемы — следовательно, у вас проблемы с проектированием, причем и "в других языках" тоже.
А>3. невозможность создать обычную plain структуру данных. Это раздражает. Приходится заводить пустой класс, создавать его и присваивать ему атрибуты (так советуют в официальном python tutorial). Т.е. потом, когда придется читать код, вместо чтения просто объявления структуры, придется рыскать по коду и смотреть, что же там присваивается.
Классы в питоне это словари, а геттеры/сеттеры атрибутов реализуются через соответствующие методы.
Примерно так:
class PlainDumbStructure(object):
def __init__(self, **kw):
# заведем отдельный словарь для наших атрибутов, что бы не вызывались обработчики
# при обращении к нему. может быть, можно и с __dict__, но надо проверять.
self.vars.update(kw)
def __getter__(self, name):
return self.vars[name] # бросит KeyError если нет такой записи в словаре, можете обработать и кидать AttributeErrordef setter(self, name, val):
self.vars[name] = val
# использование:
pod = PlainDumbStructure(lala="foo", bebe="bar", foo=42)
pod.some_new_attribute = 88
pod.another_attribute = 14
Можно сделать примерно такой конструктор метакласса any_class:
def any_class(name):
o = new.classobj(name, (PlainDumbStructure), {'__init__':lambda self,**kw: self.vars.update(kw)})
module = inspect.getmodule(o)
if not getattr(module, o.__name__, None):
setattr(module, o.__name__, o)
return getattr(module, o.__name__)
# использование:
pechen_treski = any_class("Pechen_Treski") #
pod = pechen_treski(a=1, b=2)
pod.c = 4
должно работать, но не запускал. Идея совершенно точно рабочая, any_class из нашего проекта, но в девичестве он наследовался от object, а не от PlainDumbStructure
А>5. Интерфейсы. Это действительно полезная фича из с++/java
Насчет "действительной полезности" это тема для отдельного разговора, но "it's python, baby. use duck typing or die"
А>и наследую от него. Но в таком случае у меня нет никакого способа сказать, что, скажем в функцию должен передаваться класс, наследующий от этого интерфейса.
Динамическая типизация и duck typing, что вы хотите? Нужна сильная статическая типизация — используйте haskell и ocaml (особенно, если производительность — это issue)
А>6. очень плохая производительность при работе с матрицами (линейная алгебра. я использую numpy.linalg). Ну тут непонятно до какой степени виноват питон, а до какой степени lapack/blas которые идут с ним в комплекте. (матлаб поставляется с MKL, которая является самой быстрой на intel, но к сожалению она закрыта)
Ну если они спроектированы и используются так, что в сишные вызовы отдаются большие куски данных и там процессятся — то будет быстро, а если нативные методы зовутся из циклов на питоне для процессинга малого количества данных — то будет медленно. Питон сам для числодробительных задач не приспособлен, а нативные вызовы имеют накладные расходы. Обобщайте, выносите крупные блоки кода в си, зовите из питона. Или используйте ocaml haskell.
class PlainDumbStructure(object):
def __init__(self, **kw):
# заведем отдельный словарь для наших атрибутов, что бы не вызывались обработчики
# при обращении к нему. может быть, можно и с __dict__, но надо проверять.
self.vars.update(kw)
def __getattr__(self, name):
return self.vars[name] # бросит KeyError если нет такой записи в словаре, можете обработать и кидать AttributeErrordef __setattr__(self, name, val):
self.vars[name] = val
# использование:
pod = PlainDumbStructure(lala="foo", bebe="bar", foo=42)
pod.some_new_attribute = 88
pod.another_attribute = 14
А>сразу оговорюсь, у меня довольно специфический взгляд на питон. Я не программист, точнее, не совсем программист: я статистически обрабатываю (исследую, сторю модели) данные с некоторых real-time систем. Данных очень много, терабайты, потому "руками" делать ничего не получится.
Посмотрите на sage. Python + огромное количество нативных библиотек для математической обработки данных.
А>1. отладка.... Т.е. если ошибка происходит, то интерпретатор дается в "module scope" а не непосредственно после той строчки, где была ошибка.
Запускайте с помощью iphyton. При возникновении ошибки по команде %debug попадете в отладчик.
А>2. видимость переменных...
Не используйте глобальные переменные. Если используете внутри модуля, давайте им названия начинающихся с двух символов подчеркивания.
А>3. невозможность создать обычную plain структуру данных.
Так что ли не устраивает?
А>5. Интерфейсы. Это действительно полезная фича из с++/java
В python есть Duck typing + множественное наследование и в послденей версии абстрактные классы, интерфейсам тут просто нет места.
А>6. очень плохая производительность при работе с матрицами (линейная алгебра. я использую numpy.linalg). Ну тут непонятно до какой степени виноват питон, а до какой степени lapack/blas которые идут с ним в комплекте. (матлаб поставляется с MKL, которая является самой быстрой на intel, но к сожалению она закрыта)
Основной часто встречающийся оверхед, это преобразование данных между питоном и внешними процедурами. Два вызова встроенных функций пробегающих массив дважды, бывает на порядок быстрее чем один проход for'ом и внешнее преобразование одиночного элемента.
-- Главное про деструктор копирования не забыть --
Re[2]: [Python] Что мне не нравится в питоне
От:
Аноним
Дата:
18.01.09 17:50
Оценка:
Здравствуйте, dmz, Вы писали:
dmz>Я никогда не пользовался IDE и отладчиком для питона, но знаю, что подобные вещи реализуются посредством eval. Т.е. вы можете перехватить исключение, в некоем месте его обработать, вызывая eval для введенной из диалого или еще откуда-то строки, и в этой строке можете делать все, что угодно — смотреть переменные, писат в файл и т.п. На таком принципе работают, например, встроенные отладчики веб-фреймворков. Так же, попробуйте использовать ipython для REPL, и, возможно, отладки в этом стиле.
Спасибо, я видел IPython, но не заметил там такой функциональности. Т.к. я работаю в emacs, то мне он вполне подходит
dmz>Естественно, она же в той же области видимости, что и они. Это более логичное поведение, чем, скажем, в си, где функции, определенные ниже переменной, ее видят, а ниже — нет. Так как модуль является структурной единицей программы, а номер строки — нет. Если подобное поведение вызывает у вас проблемы — следовательно, у вас проблемы с проектированием, причем и "в других языках" тоже.
Ну я же сказал, что огромные системы не пишу. Редко когда больше 100к на проект. А с видимостью пока борюсь так
if __name__ == '__main__':
main()
dmz>Классы в питоне это словари, а геттеры/сеттеры атрибутов реализуются через соответствующие методы.
выглядит как какой-то костыль, нет? приходится использовать метаклассы, чтобы просто аналог сишного struct создать?
хотя за код спасибо
А>>5. Интерфейсы. Это действительно полезная фича из с++/java
dmz>Насчет "действительной полезности" это тема для отдельного разговора, но "it's python, baby. use duck typing or die"
если честно термин duck typing услышал только сейчас
наверное действительно надо мозги перестраивать
dmz>Ну если они спроектированы и используются так, что в сишные вызовы отдаются большие куски данных и там процессятся — то будет быстро, а если нативные методы зовутся из циклов на питоне для процессинга малого количества данных — то будет медленно. Питон сам для числодробительных задач не приспособлен, а нативные вызовы имеют накладные расходы. Обобщайте, выносите крупные блоки кода в си, зовите из питона. Или используйте ocaml haskell.
ну у меня с этим проблема была скорее в том плане, что приходится помучиться чтобы нормальный blas\lapack к питону прикрутить. в тех конкретных проектах, которые я делал накладные расходы от питона небольшие.
Здравствуйте, Аноним, Вы писали:
А>5. Интерфейсы. Это действительно полезная фича из с++/java А>Для того чтобы в питоне мне сделать интерфейс я создаю такой класс А>class IForecastingStrategy: А> def forecast(data): А> raise NotImplementedError А>и наследую от него. Но в таком случае у меня нет никакого способа сказать, что, скажем в функцию должен передаваться класс, наследующий от этого интерфейса.
Можно сделать так:
def f(x):
if not isinstance(x, IForecastingStrategy):
raise RuntimeError("Invalid type",x)
x.forecast(data)
Это можно запаковать в декоратор, или воспользоваться готовой библиотекой подобного типа
Re[2]: [Python] Что мне не нравится в питоне
От:
Аноним
Дата:
18.01.09 18:01
Оценка:
Здравствуйте, kmmbvnr, Вы писали:
K>Посмотрите на sage. Python + огромное количество нативных библиотек для математической обработки данных.
я видел его, но если честно мне он не очень нравится. сомневаюсь, что у sage большое будущее. В отличие от scipy
K>Запускайте с помощью iphyton. При возникновении ошибки по команде %debug попадете в отладчик.
спасибо, попробую!
А>>2. видимость переменных... K>Не используйте глобальные переменные. Если используете внутри модуля, давайте им названия начинающихся с двух символов подчеркивания.
А>>3. невозможность создать обычную plain структуру данных. K>Так что ли не устраивает?
K>
thnx, не знал про __slots__. Странно что в python tutorial предлагают другое решение.
K>Основной часто встречающийся оверхед, это преобразование данных между питоном и внешними процедурами. Два вызова встроенных функций пробегающих массив дважды, бывает на порядок быстрее чем один проход for'ом и внешнее преобразование одиночного элемента.
ну с этим я вроде бы разобрался (пересобиранием atlas)
А>Ну я же сказал, что огромные системы не пишу. Редко когда больше 100к на проект. А с видимостью пока борюсь так
100KLOC на питоне это довольно прилично, вообще то.
А>выглядит как какой-то костыль, нет? приходится использовать метаклассы, чтобы просто аналог сишного struct создать? А>хотя за код спасибо
Ну, предполагается, что в ОО структуры не нужны, но если очень хочется, то можно сделать. Да и метакласс там вторичен — мне
он понадобился для того, что бы порождать новый класс any_class("klazz")(a=1, b=2) в в любом нужном месте. Аналог структуры можно
сделать и без него.
А>наверное действительно надо мозги перестраивать
Ну в общем, дизайнить в стиле джавы тут не имеет смысла.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, kmmbvnr, Вы писали:
K>>Посмотрите на sage. Python + огромное количество нативных библиотек для математической обработки данных. А>я видел его, но если честно мне он не очень нравится. сомневаюсь, что у sage большое будущее. В отличие от scipy
Sage это просто python + множество библиотек "из коробки", в том числе и scipy.
Не понимаю, что там могло не понравиться.
-- Главное про деструктор копирования не забыть --
Здравствуйте, Аноним, Вы писали:
А>1. отладка. когда в матлабовской программе происходит критическая ошибка, то матлаб вываливается и дает мне интерпретатор в месте, где ошибка произошла.
ipython, %pdb маджик
А>2. видимость переменных. Если переменная определена в "module scope", то все вызываемые функции ее видят!
+1, global это кривизна еще та.
А>3. невозможность создать обычную plain структуру данных. Это раздражает. Приходится заводить пустой класс, создавать его и присваивать ему атрибуты (так советуют в официальном python tutorial). Т.е. потом, когда придется читать код, вместо чтения просто объявления структуры, придется рыскать по коду и смотреть, что же там присваивается.
Если нужно обьявить структуру, то описания класса с ней занимает numberOfFields + 2. То есть реально оверхэд в одну строчку(__init__), неужели это так много?
А>5. Интерфейсы. Это действительно полезная фича из с++/java А>...Но в таком случае у меня нет никакого способа сказать, что, скажем в функцию должен передаваться класс, наследующий от этого интерфейса.
Это проблема динамической типизация, а не Питона.
А>6. очень плохая производительность при работе с матрицами (линейная алгебра. я использую numpy.linalg). Ну тут непонятно до какой степени виноват питон, а до какой степени lapack/blas которые идут с ним в комплекте. (матлаб поставляется с MKL, которая является самой быстрой на intel, но к сожалению она закрыта)
Можно детали, интересует порядок разницы с матлабом и где конкретно тормоза. Не смотрел в сторону PyCUDA?
Re[2]: [Python] Что мне не нравится в питоне
От:
Аноним
Дата:
08.02.09 16:41
Оценка:
N>Можно детали, интересует порядок разницы с матлабом и где конкретно тормоза. Не смотрел в сторону PyCUDA?
Сорри, что отвечаю так поздно.
разница с python+scipy "из коробки" против matlab "из коробки" для матриц порядка 1000 будет большой, где-то в 5-6 раз "питон" медленнее. Более того, что самое плохое, он асимптотически хуже, т.е. чем больше размер матрицы тем хуже "питон". Везде ставлю питон в кавычках, потому что на самом деле это не проблема питона, а той реализации blas/lapack с которая с ним идет по дефолту. C матлабом идет проприетарная intel MKL, которая значительно быстрее. Т.е. если привязать питон к intel MKL, то для больших матриц разницы не должно быть (имеются в виду именно элементарные операции типа LU, QR, SVD decompositions и прочие. Для решения систем уравнений, скажем, матлаб все равно может быть быстрее из-за эвристики)
Про PyCUDA... Это немножко не то, к тому же работает только на определенных карточках.
К тому же кто-то из банка у нас смотрел эту вещь (для матлаба правда http://www.accelereyes.com/) — для наших целей это не очень подходит, т.к. там где-то есть проблемы с доступной памятью. Хотя деталей я не знаю.
Здравствуйте, dmz, Вы писали:
А>>2. видимость переменных. Если переменная определена в "module scope", то все вызываемые функции ее видят!
Не все. См. мой пример ниже.
dmz>Естественно, она же в той же области видимости, что и они. Это более логичное поведение, чем, скажем, в си, где функции, определенные ниже переменной, ее видят, а ниже — нет. Так как модуль является структурной единицей программы, а номер строки — нет. Если подобное поведение вызывает у вас проблемы — следовательно, у вас проблемы с проектированием, причем и "в других языках" тоже.
Э... тут есть тонкий момент
x = 123 # глобальнаяdef getx() :
return x
# по аналогии, определим встречную функциюdef putx(xx) :
x = xx # присвоили локальной переменной :( - wish you happy debug
# правильно делать вот такdef setx(xx) :
global x
x = xx
# ну и ещё пара очень похожих функцийdef next() :
y = x+1
return y
def incx_bad() :
y = x+1 # ошибка :) локальная переменная x используется до инициализации
x = y
return y
# правильно такdef incx() :
global x
y = x+1
x = y
# или просто x+=1, но я сохранил для единообразияreturn y
Нельзя сказать, что очень прозрачно. И дело не в проектировании. Точнее, дело в проектировании языка
Здравствуйте, Аноним, Вы писали:
N>>Можно детали, интересует порядок разницы с матлабом и где конкретно тормоза. Не смотрел в сторону PyCUDA?
А>Везде ставлю питон в кавычках, потому что на самом деле это не проблема питона, а той реализации blas/lapack с которая с ним идет по дефолту. C матлабом идет проприетарная intel MKL, которая значительно быстрее.
Понятно. Хотя вроде MKL прикрутить не сложно.
А>Про PyCUDA... Это немножко не то, к тому же работает только на определенных карточках. А>для наших целей это не очень подходит, т.к. там где-то есть проблемы с доступной памятью.
Ну у вас и матрицы, если в 1.5 гига на Тесле не влезают! У меня с ней проблемы в основном из за single-precision.
К>Нельзя сказать, что очень прозрачно. И дело не в проектировании. Точнее, дело в проектировании языка
А есть примеры, как сделать лучше? В C++, если мне не изменяет, проблема решалась оператором
разрешения области видимости ::. Так что global x — можно считать неким аналогом. Кроме того, не
исключено, что в питоне можно к глобальной переменной обратиться, указав полное имя (с модулем). Не
уверен, проверять не хочется. Использование такого рода глобальных переменных — сам по себе просчет,
так что если их не использовать, то и проблем не возникает. Другое дело что для скрипта это неприемлемо,
а питон из чисто скриптового и glue языка вырос в то, во что вырос.
dmz>А есть примеры, как сделать лучше? В C++, если мне не изменяет, проблема решалась оператором разрешения области видимости ::. Так что global x — можно считать неким аналогом.
Особенность С++ — в том, что переменные там только ищутся, а не вводятся "на ходу".
В питоне объявление переменной — это либо global x, либо x = ..., то есть объявление и присваивание выглядят одинаково.
Просто изумительное поле для очепяток.
Было бы логично воплотить одно из поведений
— явно объявлять переменные (var x = ...)
— искать/объявлять переменные по первому использованию в контексте (это тоже ошибкоопасно, но, по крайней мере, локально)
К>В питоне объявление переменной — это либо global x, либо x = ..., то есть объявление и присваивание выглядят одинаково. К>Просто изумительное поле для очепяток.
Весь питон — одно большое изумительное поле для опечаток, ибо динамический язык.
In [4]: x = 10
In [5]: [x for x in xrange(0, 10)]
Out[5]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [6]: x
Out[6]: 9
Кто бы мог подумать... Если бы язык делали нормальные люди, переменная была бы локальна в
области list comprehension например.
Здравствуйте, dmz, Вы писали:
dmz>Кто бы мог подумать... Если бы язык делали нормальные люди, переменная была бы локальна в области list comprehension например.
Это что, дефект спецификации языка или так и задумано ?
dmz>>Кто бы мог подумать... Если бы язык делали нормальные люди, переменная была бы локальна в области list comprehension например.
К>Это что, дефект спецификации языка или так и задумано ?
Я где-то когда-то давно читал какую-то отмазку, почему это так сделано, но если слить воду, то
(исключительно по моему субъективному мнению) останется "ниасилили и забили".
Либо если это починить, то что-то другое сломается. То ли успели наколбасить скриптов, которые используют такое поведение.
Или это у них на момент создания был аналог цикла со счетчиком (но непонятно откуда они его унаследовали такой, в таком случае)
Здравствуйте, dmz, Вы писали:
dmz>Кто бы мог подумать... Если бы язык делали нормальные люди, переменная была бы локальна в dmz>области list comprehension например.
Проверил в консольке Python 3.0, похоже что эта фича исправлена.