Comments 81
супер
+1
Утрированный пример на самом деле решается так:
По сравнению с:
my_function(sum(x for x in xrange(100) if x % 3 == 1))
По сравнению с:
[x for x in range(100)] | where(lambda x: x % 3 == 1) | sum | my_function
+5
Неправда ваша, было бы так:
range(100) | where(lambda x: x % 3 == 1) | add | my_function
+2
Пайпы читать проще и редактировать. Даже если они и длиннее выглядят. Поток данных проще: слева направо. А в Вашем примере надо сначала забежать глазами внутрь скобок, потом обратно.
+2
total = sum(x for x in range(100) if x % 3 == 1)
result = my_function(total)
Читаемо? Даже очень. Лично я не люблю костыли. Пайпы это хорошо только в баше. И это НЕ pythonic way.
+2
Ну, а теперь представьте, что нужно связать около 10-ка функций. Будет очень громоздко. А чем перегрузка операторов не pythonic way? Вроде же, это у них там стандартный механизм… Ну. Насколько мне известно…
0
Ну. Это же не объективные понятия: простой, незагрязнённый и понятный. Кому-то pipe'ы понятнее и проще: зачем я должен придумывать имена временных переменных, если за меня это может сделать библиотека. Это проще, на мой вкус. И тут нет призыва использовать все средства. Используется одно из средств, удобным во многих ситуациях способов. И на самом деле, тут ничего нестандартного и страшного нет, потому что вот этот pipe — вполне себе чуть ограниченная воплощение стандартной концепции монады, которая считается хорошим инструментом программирования.
+1
Есть один момент — обычно, если есть около 10-ка функций идущих подряд, и это независимые функции, а не запрос, как в примере(select… where ...), то при отладке всегда нужно проверить результат каждого промежуточного вызова. И я, лично, вряд ли бы писал сначала по одному:
b=fun1(a)
c=fun2(b)
а в релизном варианте переписывал в виде с = fun1(a) | fun2()
Во-первых долго, во-вторых, мало ли, вдруг потом ещё придется мне, или кому-то другому, отлаживать программу.
b=fun1(a)
c=fun2(b)
а в релизном варианте переписывал в виде с = fun1(a) | fun2()
Во-первых долго, во-вторых, мало ли, вдруг потом ещё придется мне, или кому-то другому, отлаживать программу.
+1
Не надо ничего переписывать. Тестируйте каждый пайп в отдельности, а потом всю цепочку в совокупности. Привет юниттестам! В этом смысле никакой разницы с простыми функциями.
0
Это прикольно, но это не python way, это ruby way.
0
Чё-то это уже странные утверждения, imho. Если это написано на Python, то какой нафиг Ruby way? По-вашему, что? Вообще никакую библиотеку нельзя использовать, потому что там какие-то функции понаписаны, и никто не поймёт, что происходит, читая код, в котором они используются.
Мда… Фанаты — люди странные. Сделан классный механизм, стандартными средствами их любимого языка… Радоваться надо, разве нет? А то, что какие-то там проблемы могут быть, да… Могут. Но это повод библиотеку развивать, а не трясти перед ней python way'ем.
Эх :( Вы убили во мне желание воспользоваться в следующем проекте Python'ом. Какое-то у него Дао странное. Буду юзать Lua.
Мда… Фанаты — люди странные. Сделан классный механизм, стандартными средствами их любимого языка… Радоваться надо, разве нет? А то, что какие-то там проблемы могут быть, да… Могут. Но это повод библиотеку развивать, а не трясти перед ней python way'ем.
Эх :( Вы убили во мне желание воспользоваться в следующем проекте Python'ом. Какое-то у него Дао странное. Буду юзать Lua.
0
Ну какие фанаты, ну что это за разбрасывание лейбами? «Любители метапрограммирования — люди странные. Можно написать так просто, так нет же, дай сделать что-нибудь позапутаннее.»
Я вот сейчас поддерживаю большую довольно кодовую базу на питоне (80к+ SLoC, для питона это — очень много), и надо сказать, что любая попытка внести маленькие приятные штуки оказывается приятной только для автора. Или, другими словами, задолбало находить странные потуги чуваков на метапрограммирование. Хочется оторвать руки и пришить что-то, что будет выдавать поддерживаемый код, а не отрыжки мамонта.
Да, можно и с метапрограммированием писать поддерживаемый код. Вопрос лишь в самодисциплине, конечно. И за значения по умолчанию я принимаю то, что у большинства самодисциплина плоха.
Я вот сейчас поддерживаю большую довольно кодовую базу на питоне (80к+ SLoC, для питона это — очень много), и надо сказать, что любая попытка внести маленькие приятные штуки оказывается приятной только для автора. Или, другими словами, задолбало находить странные потуги чуваков на метапрограммирование. Хочется оторвать руки и пришить что-то, что будет выдавать поддерживаемый код, а не отрыжки мамонта.
Да, можно и с метапрограммированием писать поддерживаемый код. Вопрос лишь в самодисциплине, конечно. И за значения по умолчанию я принимаю то, что у большинства самодисциплина плоха.
+1
Метапрограммирование? Это что такое? Декораторы? Метаклассы? Обычная весьма вещь, в любом коде встречается, множество фреймворков это использует.
Тут другой вопрос. Что есть хороший программист? Тот, который, вникая в систему и внося в нее изменения, не нарушает архитектуру и внутренние соглашения.
Что есть великолепный программист? Тот, кто может, усвоив правила большой системы, увеличить внутреннюю корректность, не нарушая цельности.
Что есть плохой программист? Тот, кто использует приемы, идущие в разрез с архитектурой системы; и не принимающий общие решения.
И метапрограммирование не имеет к этому никакого отношения.
Тут другой вопрос. Что есть хороший программист? Тот, который, вникая в систему и внося в нее изменения, не нарушает архитектуру и внутренние соглашения.
Что есть великолепный программист? Тот, кто может, усвоив правила большой системы, увеличить внутреннюю корректность, не нарушая цельности.
Что есть плохой программист? Тот, кто использует приемы, идущие в разрез с архитектурой системы; и не принимающий общие решения.
И метапрограммирование не имеет к этому никакого отношения.
0
Я бы сказал, что метапрограммирование — это построение DSL. Внутренних или внешних.
А декораторы-метаклассы — всего лишь детали реализации. Подмножество используемых для метапрограммирования инструментов.
А декораторы-метаклассы — всего лишь детали реализации. Подмножество используемых для метапрограммирования инструментов.
0
Вероятно, это прозвучит забавно, но… Разве любое программирование API, уж тем более вблизи парадигмы ООП, — не то ли самое построение DSL?
Метапрограммирование в случае Питона — и не только его самого — это страшное название для простой вещи: изменения в поведении классов, будь то создание экземпляров класс, удаление, использование с операторами. Конечно, с такими вещами следует быть аккуратным. Но не более того!
И вообще буду строгим :). Признаться, после ознакомления с «DSL» на примере Scala, Haskell и Ruby, я перестал понимать, откуда взялась в специальной прессе и блогосфере такая шумиха вокруг этого неопределенного термина; так же посмеиваюсь над теми, кто делает идола из ООП-подхода. Подумаешь, догадались данные складывать в кучку с функциями для работы с ними! Полиморфизм — просто издержки жесткой системы типов; инкапсуляция — просто хорошая практика программирования; наследование — не самый удачный способ менять поведение объектов класса.
И все. :)
Метапрограммирование в случае Питона — и не только его самого — это страшное название для простой вещи: изменения в поведении классов, будь то создание экземпляров класс, удаление, использование с операторами. Конечно, с такими вещами следует быть аккуратным. Но не более того!
И вообще буду строгим :). Признаться, после ознакомления с «DSL» на примере Scala, Haskell и Ruby, я перестал понимать, откуда взялась в специальной прессе и блогосфере такая шумиха вокруг этого неопределенного термина; так же посмеиваюсь над теми, кто делает идола из ООП-подхода. Подумаешь, догадались данные складывать в кучку с функциями для работы с ними! Полиморфизм — просто издержки жесткой системы типов; инкапсуляция — просто хорошая практика программирования; наследование — не самый удачный способ менять поведение объектов класса.
И все. :)
0
Лично у меня эти понятия различаются «в голове».
Можно делать ООП на С. А можно и на С++ писать «с классами», но без ООП.
DSL и ООП предполагают несколько отличающийся способ мышления.
Т.е. если я делаю внутренний DSL на Питоне — то использую метаклассы, декораторы, перегрузку операторов. Главное — думаю о получившейся конструкции как о создании нового микроязыка (не выходящего за синтаксис Питона, конечно). Вот и вся разница.
Можно делать ООП на С. А можно и на С++ писать «с классами», но без ООП.
DSL и ООП предполагают несколько отличающийся способ мышления.
Т.е. если я делаю внутренний DSL на Питоне — то использую метаклассы, декораторы, перегрузку операторов. Главное — думаю о получившейся конструкции как о создании нового микроязыка (не выходящего за синтаксис Питона, конечно). Вот и вся разница.
0
Но согласитесь, понятия различаются очень условно! Можно даже сказать, что DSL — это просто что-то вроде фразы «надо стремиться говорить на языке целевой области». А уж средства… ООП, функциональный подход, метапрограммирование, макросы (даже в Си/Си++ что-то похожее на DSL с их помощью вытворяют )… Кто во что горазд.
В рамках концепции ООП очень даже можно писать DSL. Скажем, тот же злосчастный джанговский ORM; многие другие реализации ActiveRecord — очень даже себе DSL.
Для меня твердой грани давно уже нет; видать слегка подпортил себе мышление обилием опробованных языков :-) DSL — это просто одна из задач, что иногда встречается.
В рамках концепции ООП очень даже можно писать DSL. Скажем, тот же злосчастный джанговский ORM; многие другие реализации ActiveRecord — очень даже себе DSL.
Для меня твердой грани давно уже нет; видать слегка подпортил себе мышление обилием опробованных языков :-) DSL — это просто одна из задач, что иногда встречается.
0
Полностью согласен, различие небольшое.
О метапрограммировании на C++. Не могу удержаться:
www.boost.org/doc/libs/1_46_1/doc/html/xpressive/user_s_guide.html#boost_xpressive.user_s_guide.introduction
И связка с Питоном:
www.boost.org/doc/libs/1_46_1/libs/python/doc/tutorial/doc/html/python/exposing.html
Это не макросы, все гораздо хуже — шаблоны с частичной специализацией.
О метапрограммировании на C++. Не могу удержаться:
www.boost.org/doc/libs/1_46_1/doc/html/xpressive/user_s_guide.html#boost_xpressive.user_s_guide.introduction
И связка с Питоном:
www.boost.org/doc/libs/1_46_1/libs/python/doc/tutorial/doc/html/python/exposing.html
Это не макросы, все гораздо хуже — шаблоны с частичной специализацией.
0
Хм. Особых проблем с отладкой pipe'ов в том же Bash нет. А вот эти Python-пайпы всяко более удобны.
0
Удобны более чем что? Чем непайпы?
В баше проблем нет, потому что нет и многого другого. Перегрузки операторов, например.
В баше проблем нет, потому что нет и многого другого. Перегрузки операторов, например.
0
В bash как языке есть множество своих проблем: архаичный синтаксис, много избыточности и бла-бла-бла.
С другой стороны, в bash как инструменте для работы в командной строке есть много плюсов: простота, хорошая работа с текстом. Да и просто обмен данными через stdin/stdout, на мой взгляд — это блестяще. не говоря уже о десятилетиях добрых *никсовых традиций и лучших практик.
Однако, здесь речь идет прежде всего об использовании хорошо знакомого любым программистам синтаксиса для построения схожей абстракции на языке общего назначения…
Лично мне это интересно прежде всего в качестве элемента более крупной фреймворка или либы для поэтапной обработки данных, но никак не в качестве замены баша.
С другой стороны, в bash как инструменте для работы в командной строке есть много плюсов: простота, хорошая работа с текстом. Да и просто обмен данными через stdin/stdout, на мой взгляд — это блестяще. не говоря уже о десятилетиях добрых *никсовых традиций и лучших практик.
Однако, здесь речь идет прежде всего об использовании хорошо знакомого любым программистам синтаксиса для построения схожей абстракции на языке общего назначения…
Лично мне это интересно прежде всего в качестве элемента более крупной фреймворка или либы для поэтапной обработки данных, но никак не в качестве замены баша.
0
Да Bash тут только при том, что с отладкой проблемы не такие уж и большие возникают.
А с синтаксисом… Ну, я ещё повторюсь: человек хочет сделать стандартную математическую абстракцию — композицию функций. Хочет выразить её через оператор, чтобы не писать f.compose(f1).compose(f2).compose(f3)… * использовать не разумно, потому что она часто встречается в коде. А |, известно из Bash, вполне себе уживается с редко используемым битовым or. Всё, imho, рационально.
Вы же, наверное, не критикуете библиотеки за то, что в них перегружают арифметику для работы с матрицами или длинными числами. Или когда + используется для конкатенации строк, или когда то же | используется для записи грамматических правил.
А с синтаксисом… Ну, я ещё повторюсь: человек хочет сделать стандартную математическую абстракцию — композицию функций. Хочет выразить её через оператор, чтобы не писать f.compose(f1).compose(f2).compose(f3)… * использовать не разумно, потому что она часто встречается в коде. А |, известно из Bash, вполне себе уживается с редко используемым битовым or. Всё, imho, рационально.
Вы же, наверное, не критикуете библиотеки за то, что в них перегружают арифметику для работы с матрицами или длинными числами. Или когда + используется для конкатенации строк, или когда то же | используется для записи грамматических правил.
0
Пытаюсь вспомнить, в каком коде я видел f.compose(f1).compose(f2).compose(f3)… *
Нет, не вспоминается.
Нет, не вспоминается.
0
Зато что-то вроде
писал не раз. Обратите внимание: композиция функций с использованием круглых скобок, таких привычных и родных.
f.compose(
f1,
f2,
f3(arg)
)
писал не раз. Обратите внимание: композиция функций с использованием круглых скобок, таких привычных и родных.
0
Ай… Право же… Какой-то странный критерий оценки. Почему привычность должна быть приоритетнее удобства? Вероятно, нам с Вами друг друга просто не понять.
0
Нет. Ну в самом деле. А почему вы не настаиваете на том, чтобы вместо A * B + C писать: A.mul(B).add©? Привычнее же. Сплошные скобки… Прямо Lisp — классика.
0
Не чем 'непайпы', а как способ композиции функций. При чём тут перегрузка операторов — не очень понятно. Есть в математике такая стандартная операция — композиция. Философские причины того, почему в языке может быть перегружен оператор * для умножения матриц, а оператор | не может быть перегружен для композиции функций — лично для меня загадочны.
0
Перегружать — можно. Делайте на здоровье.
Используйте оператор | на полную катушку. Через год-два посчитайте, сколько раз за это время эта штука вам понадобилась. Перечитайте еще раз код, который эти пайпы использовал. И напишите success story, как все было хорошо и здорово, что вы уже не представляете себе жизни без чудо-механизма.
Используйте оператор | на полную катушку. Через год-два посчитайте, сколько раз за это время эта штука вам понадобилась. Перечитайте еще раз код, который эти пайпы использовал. И напишите success story, как все было хорошо и здорово, что вы уже не представляете себе жизни без чудо-механизма.
0
Это действительно гениально, причем и как идея, так и реализация! Я в восторге!
Обязательно начну использовать у себя.
Обязательно начну использовать у себя.
0
Не надо, пожалейте тех, кто будет работать с вами.
+3
Никто не будет, я буду использовать в СВОИХ скриптах, которыми пользуюсь только я. Конечно же нельзя использовать pipe в проектах, код которых будет использовать кто-то ещё.
0
Главное — создать свой объект, т.к. в Питоне нельзя расширять предопределённые типы :(((. А трубы — это по практически то же, что и вызов методов:
[1,2,3,4] | where(lambda x: x<=2) | as_list
легко реализуется «питоническим» способом как:
KribleKrable([1,2,3,4]).where(lambda x: x<=2).as_list()
[1,2,3,4] | where(lambda x: x<=2) | as_list
легко реализуется «питоническим» способом как:
KribleKrable([1,2,3,4]).where(lambda x: x<=2).as_list()
0
Есть неприятный нюанс. В каком namespace будет искать имена ваш KribleKrable?
Я имею в виду: как найдутся функции where и as_list из примера?
Я имею в виду: как найдутся функции where и as_list из примера?
0
where и т.п. — методы объекта, возвращаемого функцией или конструктором KribleKrable. А сама KribleKrable ищется, очевидно, в том же пространстве имён, что и where из трубного примера.
0
«трубный» пример будет последовательно искать в локальном, глобальном и builtin пространствах имён.
Т.е. можно писать свои «трубы», оборачивая их декоратором @Pipe.
Наличие декоратора некрасиво, ну да ладно.
KribleKrable же должна явно содержать все доступные методы. Можно, конечно, выкрутиться.
1. Перегрузить __getattribute__ (или __getattr__ — не важно).
2. Хакнуть фрейм и вытащить f_globals/f_locals.
3. Эмулировать путь поиска имени.
Но это запутанная процедура, не делающая прочтение кода яснее — слишком логика отличается от привычной. Мне не нравится. Впрочем, «трубный» пример не нравится тоже.
Т.е. можно писать свои «трубы», оборачивая их декоратором @Pipe.
Наличие декоратора некрасиво, ну да ладно.
KribleKrable же должна явно содержать все доступные методы. Можно, конечно, выкрутиться.
1. Перегрузить __getattribute__ (или __getattr__ — не важно).
2. Хакнуть фрейм и вытащить f_globals/f_locals.
3. Эмулировать путь поиска имени.
Но это запутанная процедура, не делающая прочтение кода яснее — слишком логика отличается от привычной. Мне не нравится. Впрочем, «трубный» пример не нравится тоже.
0
Сомнительный запашок у получающегося кода.
Перегрузка операций (любых) — не самый лучший способ построения каскадов генераторов.
Из-за этой перегрузки в записи возможны досадные ошибки.
Понятность снижается (а должна бы увеличиваться).
Оператор | может встречаться и в привычном контексте битовых операций.
Здесь же придется внимательно смотреть: это pipe или нет?
Если, скажем, numpy перегружает операторы для матриц — там ясна предметная область и не происходит
её перекрытия со стандартной семантикой. Если складываются два числа — они делают это по правилам элементарной арифметики. Две матрицы — тоже ничего нового.
В sqlalchemy для SQL запросов перегружаются все битовые операции.
При этом биты для чисел и запросы для SQL разведены по разным углам, очень редко пересекаясь в коде. По выражению практически всегда легко понять, где кто.
Здесь же | работает как битовый для чисел и pipe в стиле bash если самый правый операнд оказался типа Pipe.
Семантика разная, и две семантики легко могут пересекаться. Это неприятно.
Еще одно: эту штуку крайне легко сломать.
Вроде бы класс L вполне невинен. Только, на беду, содержит оператор __or__, приоритет которого выше чем у
__ror__. Нехорошо…
Не все возможные занятные выкрутасы стоит использовать.
Сугубо личное мнение. Если кто не согласен — навязывать свое мнение не буду.
Раньше мне тоже нравились подобные полеты мысли: чем замудрённей — тем интересней.
Сейчас скорее признаю предлагаемый код unpythonic.
Перегрузка операций (любых) — не самый лучший способ построения каскадов генераторов.
Из-за этой перегрузки в записи возможны досадные ошибки.
Понятность снижается (а должна бы увеличиваться).
Оператор | может встречаться и в привычном контексте битовых операций.
Здесь же придется внимательно смотреть: это pipe или нет?
Если, скажем, numpy перегружает операторы для матриц — там ясна предметная область и не происходит
её перекрытия со стандартной семантикой. Если складываются два числа — они делают это по правилам элементарной арифметики. Две матрицы — тоже ничего нового.
В sqlalchemy для SQL запросов перегружаются все битовые операции.
При этом биты для чисел и запросы для SQL разведены по разным углам, очень редко пересекаясь в коде. По выражению практически всегда легко понять, где кто.
Здесь же | работает как битовый для чисел и pipe в стиле bash если самый правый операнд оказался типа Pipe.
Семантика разная, и две семантики легко могут пересекаться. Это неприятно.
Еще одно: эту штуку крайне легко сломать.
import pipe
class L(object):
def __init__(self, v):
self.v = v
def __or__(self, r):
return 'abra'
print L(range(5)) | pipe.as_list
Вроде бы класс L вполне невинен. Только, на беду, содержит оператор __or__, приоритет которого выше чем у
__ror__. Нехорошо…
Не все возможные занятные выкрутасы стоит использовать.
Сугубо личное мнение. Если кто не согласен — навязывать свое мнение не буду.
Раньше мне тоже нравились подобные полеты мысли: чем замудрённей — тем интересней.
Сейчас скорее признаю предлагаемый код unpythonic.
+16
Согласен с вашими доводами, конечно нужно знать, что делаешь и в серьезных проектах такое использовать нельзя.
Но это же красиво! Это «just for fan»! Почему бы и не поиспользовать это в своих скриптах? На мой взгляд, код многих мои скриптов с использованием pipe будет читаться и выглядеть гораздо проще и красивее, но это лишь мое мнение.
Но это же красиво! Это «just for fan»! Почему бы и не поиспользовать это в своих скриптах? На мой взгляд, код многих мои скриптов с использованием pipe будет читаться и выглядеть гораздо проще и красивее, но это лишь мое мнение.
0
Если бы мы занимались увлекательным делом «напиши как можно хитрее» — тогда да, метод хорош.
Оно только выглядит проще — а понимается на самом деле сложнее чем запись «в лоб». Мы еще не переходили к обработке ошибок и чтению traceback — вот где поле непаханое.
И всё же повторюсь еще раз: предлагаемый подход ненадежен по построению.
Можно поиграть в занятную игру: придумывание кода, который валит библиотеку или заставляет ее работать «странно». Поверьте, этого кода много. У меня сразу нарисовалось несколько безобидных на первый взгляд вариантов. В примере я привел только самый короткий и очевидный.
Оно только выглядит проще — а понимается на самом деле сложнее чем запись «в лоб». Мы еще не переходили к обработке ошибок и чтению traceback — вот где поле непаханое.
И всё же повторюсь еще раз: предлагаемый подход ненадежен по построению.
Можно поиграть в занятную игру: придумывание кода, который валит библиотеку или заставляет ее работать «странно». Поверьте, этого кода много. У меня сразу нарисовалось несколько безобидных на первый взгляд вариантов. В примере я привел только самый короткий и очевидный.
0
imho, дело не в написании хитрее. Люди ищут способы писать красивее.
Вот я уже не помню когда арфметическое или видел в живом коде. Как-то битовые операции остались в моём далеком прошлом :)
И перегрузку или тоже не припоминаю.
Хотя, всё что вы сказали — да, не добавляет радости к этой красоте.
С другой стороны, питон не призван быть супернадежным инструментом, многие вещи построены на доверии и предположении, что разработчик понимает, что он делает. А если есть сомнения — сомнений быть не должно. Команда внутри себя может дjговориться, например, что в рабочем кода нельзя использовать pipe, и делов-то.
Вот я уже не помню когда арфметическое или видел в живом коде. Как-то битовые операции остались в моём далеком прошлом :)
И перегрузку или тоже не припоминаю.
Хотя, всё что вы сказали — да, не добавляет радости к этой красоте.
С другой стороны, питон не призван быть супернадежным инструментом, многие вещи построены на доверии и предположении, что разработчик понимает, что он делает. А если есть сомнения — сомнений быть не должно. Команда внутри себя может дjговориться, например, что в рабочем кода нельзя использовать pipe, и делов-то.
+2
Множества, которые set/frozenset, тоже не используете?
> Команда внутри себя может дjговориться, например, что в рабочем кода нельзя использовать pipe, и делов-то.
Простите, потерял нить разговора. Вы предлагаете применять pipe исключительно в нерабочем коде?
> Команда внутри себя может дjговориться, например, что в рабочем кода нельзя использовать pipe, и делов-то.
Простите, потерял нить разговора. Вы предлагаете применять pipe исключительно в нерабочем коде?
+1
множества так сходу не примопню, нужно разработчиков спросить :)
эх, мне казалась мысль очень цельной.
Ок, коротко: pipe вообще не факт, что для использования в реальных проектах.
Это может быть просто как иллюстрация того, что может язык (и насколько просто он это может). Как исследование в каком направлении языку двигаться.
В конце концов — есть люди у которых ipython в качестве шела :)
эх, мне казалась мысль очень цельной.
Ок, коротко: pipe вообще не факт, что для использования в реальных проектах.
Это может быть просто как иллюстрация того, что может язык (и насколько просто он это может). Как исследование в каком направлении языку двигаться.
В конце концов — есть люди у которых ipython в качестве шела :)
+1
Кстати говоря, о множествах.
Вот если честь по чести, то эти самые множества используют тот же самый механизм переопределения __or__/__ror__; и ничего. Никто не кричит, что — О БОГИ! — программисты переопределили какую-то там побитовую операцию для каких-то там специальных типов.
Ну и что? В конце концов, реальный рабочий код пайп можно было бы разбавить нужными проверками и так далее.
Я все же утверждаю, что пайпы вполне работоспособны именно в качестве элемента какого-либо фреймворка, где можно твердо очертить область их применения.
Вот если честь по чести, то эти самые множества используют тот же самый механизм переопределения __or__/__ror__; и ничего. Никто не кричит, что — О БОГИ! — программисты переопределили какую-то там побитовую операцию для каких-то там специальных типов.
Ну и что? В конце концов, реальный рабочий код пайп можно было бы разбавить нужными проверками и так далее.
Я все же утверждаю, что пайпы вполне работоспособны именно в качестве элемента какого-либо фреймворка, где можно твердо очертить область их применения.
0
«just for fan»? Для вентилятора, пожалуй, в самый раз.
Рекомендую к просмотру и осмыслению — Ларри Гастингс: The Python That Wasn't.
Рекомендую к просмотру и осмыслению — Ларри Гастингс: The Python That Wasn't.
+5
Соглашусь с вами. Лишние выкрутасы. :)
0
Эмс… Не понятно, а в каком месте пайпы могут перемешаться с битовым or? Если утверждается, что пайпы можно только с пайпами объединять или с генераторами. То есть, пайп будет между пайпов, а битовый or будет внутри каких-нибудь скобочек внутри lambda или ещё где… В каком месте они смешаются?
0
Может быть:
[1, 2, 3] | set([3, 4])
+1
Пайпы хотелось бы объединять только с пайпами или генераторами.
А на деле запрета нет, и глюкавый код написать вполне можно.
Более того, такую проверку (забудем про тормоза на время) не очень-то и вставишь.
Чтобы все работало, нужно «генератор» заменить на «итерируемый объект» — иначе пример со списком работать не будет.
А итерируемость — слишком широкое понятие. Строка, например, подходит.
Если вспомнить, что оператор | переопределен в том числе и для множеств — проблема перестает быть академической.
Чудить можно с размахом.
Самый простой пример (он ничего не ломает, но слегка обескураживает):
А на деле запрета нет, и глюкавый код написать вполне можно.
Более того, такую проверку (забудем про тормоза на время) не очень-то и вставишь.
Чтобы все работало, нужно «генератор» заменить на «итерируемый объект» — иначе пример со списком работать не будет.
А итерируемость — слишком широкое понятие. Строка, например, подходит.
Если вспомнить, что оператор | переопределен в том числе и для множеств — проблема перестает быть академической.
Чудить можно с размахом.
Самый простой пример (он ничего не ломает, но слегка обескураживает):
from pipe import as_list
a = {1, 2, 3}
b = {4, 5}
print a | b | as_list
+2
Только, на беду, содержит оператор __or__, приоритет которого выше чем у
__ror__. Нехорошо…
Опс. Нифига себе. Спасибо.
__ror__. Нехорошо…
Опс. Нифига себе. Спасибо.
0
Кстати, вариант с KribleKrable этих недостатков лишён. А перегрузить класс и переопределить в нём часть функций можно всегда.
0
Это всё, конечно, интересно и красиво, но я бы долго моргал глазами, если бы увидел это в чужом коде.
И я уже молчу про проблемы, описанные Андреем: к чёрту этот блек-джек!
Можно было бы подумать о такой организации настоящих пайпов модуля subprocess, но нужно хорошо подумать…
И я уже молчу про проблемы, описанные Андреем: к чёрту этот блек-джек!
Можно было бы подумать о такой организации настоящих пайпов модуля subprocess, но нужно хорошо подумать…
0
Заметной разницы нету, кроме обращения порядка действий. В идеале хотелось бы что-то вроде $ в haskell — порядок остается тем же, а скобки писать не надо
0
по-моему, аналогичный эффект можно получить с помощью композиции функций. т.е. «идеалистический» код может выглядеть следующим образом:
удобно если планируется реиспользовать функцию, хотя немного громоздко для однострочников. «библиотека», в первом приближении:
wtf = compose([where(lambda x: x % 3 == 1),
sum,
my_function])
result = wtf(x for x in range(100))
удобно если планируется реиспользовать функцию, хотя немного громоздко для однострочников. «библиотека», в первом приближении:
from functools import partial
compose2 = lambda f, g: lambda *args, **kws: g(f(*args, **kws))
compose = partial(reduce, compose2)
where = partial(partial, filter) # %) ?
0
Коллеги,
как это ни странно, все перечисленные недостатки либы мне кажутся очевидным В рабочем коде я явно бы не стал использовать что-то подобное. Вообще не стал бы подключать какую угодно стороннюю либу, кроме самых необходимых, благо в стандартных батарейках вполне достаточно самого разного инструментария.
Комфортный синтаксис может быть необходимостью только в каком-то более широком контексте: фреймворке, бОльшей билиотеке, для каких-либо специальных задач.
Этот декоратор — proof of concept, не более того. Но красивый, тем и понравился :)
как это ни странно, все перечисленные недостатки либы мне кажутся очевидным В рабочем коде я явно бы не стал использовать что-то подобное. Вообще не стал бы подключать какую угодно стороннюю либу, кроме самых необходимых, благо в стандартных батарейках вполне достаточно самого разного инструментария.
Комфортный синтаксис может быть необходимостью только в каком-то более широком контексте: фреймворке, бОльшей билиотеке, для каких-либо специальных задач.
Этот декоратор — proof of concept, не более того. Но красивый, тем и понравился :)
+2
Хм…
Забавно выходит :)
Post.objects | where(user_id = 1) & where(is_published = True) | order('-time_published') | by_page(1, 10)
Забавно выходит :)
+3
Вот именно как-то так я и вижу реальное применение подхода. Аккумулировать условия внутри пайпа, вычислять только по прямому требованию.
Только, в общем, джанговский ORM так и выглядит, только через точку; принципиальной разницы здесь нет.
Как насчет потока данных из сети? Скажем, такой синтаксис:
здесь callback — сборка ответа на корректный запрос, errback — на некорректный; а оператор & — объединение корректного и некорректного обработчиков.
Только, в общем, джанговский ORM так и выглядит, только через точку; принципиальной разницы здесь нет.
Как насчет потока данных из сети? Скажем, такой синтаксис:
pipeline = socket | read_request | (callback & errback) | send_response
server.register(pipeline)
server.run(80)
здесь callback — сборка ответа на корректный запрос, errback — на некорректный; а оператор & — объединение корректного и некорректного обработчиков.
+2
Ага :) А ещё:
На самом деле у подобного подхода есть любопытное преимущество: такие цепочки можно применять к любым объектам не изменяя их функциональности. Т.е. для image в этом примере не нужно добавлять методы к классу картинки, а scale, rotate, mirrow, invert могут быть универсальными для работы с разными типами.
И неудобство правда рядом:
Так вполне может быть. Но вот залезать в глобальное пространство имён с функциями типа where, order, invert как-то некрасиво. Скорее, в реальности будет что-то типа:
А это уже не так элегантно.
image | scale(640, 960, crop = True) | rotate(PI/2) | mirrow(...) | invert()
На самом деле у подобного подхода есть любопытное преимущество: такие цепочки можно применять к любым объектам не изменяя их функциональности. Т.е. для image в этом примере не нужно добавлять методы к классу картинки, а scale, rotate, mirrow, invert могут быть универсальными для работы с разными типами.
И неудобство правда рядом:
image.scale(640, 960, crop = True).rotate(PI/2).mirrow(...).invert()
Так вполне может быть. Но вот залезать в глобальное пространство имён с функциями типа where, order, invert как-то некрасиво. Скорее, в реальности будет что-то типа:
from super.image.processor import i
image | i.scale(640, 960, crop = True) | i.rotate(PI/2) | i.mirrow(...) | i.invert()
А это уже не так элегантно.
+1
Поглядите на sqlalchemy. Там забавней — и строже одновременно.
+1
Ваша статья натолкнула меня на некоторые мысли в контексте Ruby.
В нем проблемы с пайпами как таковой нет:
записывается как:
Но с генераторами вида:
такое уже не проходит.
Приходится немножко извращаться:
Неплохо, но хочется-то предыдущий вариант! :)
Конечно, можно использовать Fiber, дополнив его методом take_while:
Работает. Но что же это получается: каждую функцию, от которой требуется свойство бесконечного генератора, внутри оборачивать в Fiber? Noway! Я слишом ленив для такого.
И тут мой взор упал на декораторы питона: «Хорошая штука» — подумал я, сейчас возьму такую же в любимом руби и… внезапно в Ruby такого не оказалось.
«Вещь то полезная… надо реализовать!» — подумал небезызвестный Yehuda Katz и забацал поддержку декораторов для руби в 80 строчек кода.
Пишем свой декоратор:
и класс для бесконечного генератора чисел фибоначчи с «чистым» методом fib:
Отлично! :)
Итого: добрая половина дня убита занятием вот этой замечательной фигней ^.^
В нем проблемы с пайпами как таковой нет:
my_function(sum(filter(lambda x: x % 3 == 1, [x for x in range(100)])))
записывается как:
my_function ((1..100) . to_a . keep_if {|x| x % 3 == 1 } . reduce(:+))
Но с генераторами вида:
def fib
a, b = 0, 1
loop do
yield a
a, b = b, a + b
end
end
fib . take_while {|x| x < 10}
такое уже не проходит.
Приходится немножко извращаться:
def fib
a, b = 0, 1
res = []
loop do
return res unless yield a
res << a
a, b = b, a + b
end
end
fib {|x| x < 10} # => [0, 1, 1, 2, 3, 5, 8]
Неплохо, но хочется-то предыдущий вариант! :)
Конечно, можно использовать Fiber, дополнив его методом take_while:
class Fiber
def take_while
res = []
loop do
val = self.resume
return res unless yield val
res << val
end
end
end
def fib
Fiber.new do
a, b = 0, 1
loop do
Fiber.yield a
a, b = b, a + b
end
end
end
fib . take_while {|x| x < 10} # => [0, 1, 1, 2, 3, 5, 8]
Работает. Но что же это получается: каждую функцию, от которой требуется свойство бесконечного генератора, внутри оборачивать в Fiber? Noway! Я слишом ленив для такого.
И тут мой взор упал на декораторы питона: «Хорошая штука» — подумал я, сейчас возьму такую же в любимом руби и… внезапно в Ruby такого не оказалось.
«Вещь то полезная… надо реализовать!» — подумал небезызвестный Yehuda Katz и забацал поддержку декораторов для руби в 80 строчек кода.
Пишем свой декоратор:
class Generator < Decorator
def call(this, *args)
Fiber.new { loop { @method.bind(this).call(*args) { |x| Fiber.yield x } } }
end
end
и класс для бесконечного генератора чисел фибоначчи с «чистым» методом fib:
class FibGen
extend MethodDecorators
Generator()
def fib
a, b = 0, 1
loop do
yield a
a, b = b, a + b
end
end
end
FibGen.new.fib . take_while {|x| x < 10} # => [0, 1, 1, 2, 3, 5, 8]
FibGen.new.fib . take(7) . map {|x| x * 2} . reduce(:+) # => 40
Отлично! :)
Итого: добрая половина дня убита занятием вот этой замечательной фигней ^.^
+1
Вижу, разгорелся небольшой спор о том, что такое pythonic и что — нет.
Четкого определения нет и быть не может.
Мое чутье основывается на многолетнем чтении рабочей переписки разработчиков питона.
И на собственном опыте, конечно.
Иногда вроде бы красивая идея отвергается по результатам обсуждения.
Причины бывают разные.
Например, предлагаемый подход работает не в том духе, в котором сделано все остальное. Это может вводить пользователя в недоумение. В малых дозах — не страшно. В больших — становится натуральным ядом.
Случается и другое: работает в большинстве очевидных случаев, но в «темных углах» поведение непредсказуемо. Более того, правильное поведение неочевидно. Или мнения делятся «пополам-на-пополам».
Значит — решение плохое. Его можно применять в сторонних библиотеках — но в стандарт оно не войдет.
Бывает и третье. Все хорошо и понятно. Во мнениях сошлись. Но генерируемые ошибки (логичные и понятные) — неочевидны. Хороший спец понимает их с полуслова. «Простой программист» теряется в догадках. Это — тоже весомый минус. Предложение не будет принято.
Иногда ван Россум говорит: «Жопой чую, что-то здесь не так. Давайте отложим решение до лучших времен. И еще раз подумаем».
Это — строгие критерии отбора в core development team. Сторонние библиотеки не обязаны им следовать.
Более того. Уверен, что я перечислил далеко не все случаи — просто вспомнил о наиболее очевидных.
Что такое pythonic или unpythonic каждый решает сам для себя. И, тем не менее, я стараюсь следовать «чувству правильного», которое крайне неочевидно. Ориентир — тот дух, что пропитывает решения создателей языка.
Не одна моя поделка при внимательном рассмотрении оказалась — unpythonic. Красивая и лаконичная запись — и затем море проблем. Что-то улучшалось, гораздо большее — выбрасывалось. Жаль, но так лучше…
Четкого определения нет и быть не может.
Мое чутье основывается на многолетнем чтении рабочей переписки разработчиков питона.
И на собственном опыте, конечно.
Иногда вроде бы красивая идея отвергается по результатам обсуждения.
Причины бывают разные.
Например, предлагаемый подход работает не в том духе, в котором сделано все остальное. Это может вводить пользователя в недоумение. В малых дозах — не страшно. В больших — становится натуральным ядом.
Случается и другое: работает в большинстве очевидных случаев, но в «темных углах» поведение непредсказуемо. Более того, правильное поведение неочевидно. Или мнения делятся «пополам-на-пополам».
Значит — решение плохое. Его можно применять в сторонних библиотеках — но в стандарт оно не войдет.
Бывает и третье. Все хорошо и понятно. Во мнениях сошлись. Но генерируемые ошибки (логичные и понятные) — неочевидны. Хороший спец понимает их с полуслова. «Простой программист» теряется в догадках. Это — тоже весомый минус. Предложение не будет принято.
Иногда ван Россум говорит: «Жопой чую, что-то здесь не так. Давайте отложим решение до лучших времен. И еще раз подумаем».
Это — строгие критерии отбора в core development team. Сторонние библиотеки не обязаны им следовать.
Более того. Уверен, что я перечислил далеко не все случаи — просто вспомнил о наиболее очевидных.
Что такое pythonic или unpythonic каждый решает сам для себя. И, тем не менее, я стараюсь следовать «чувству правильного», которое крайне неочевидно. Ориентир — тот дух, что пропитывает решения создателей языка.
Не одна моя поделка при внимательном рассмотрении оказалась — unpythonic. Красивая и лаконичная запись — и затем море проблем. Что-то улучшалось, гораздо большее — выбрасывалось. Жаль, но так лучше…
+2
Интересный подход. Но такие вещи все же надо делать на уровне языка, тогда это было бы вообще замечательно, а так это усложняет отладку кода и понимание кода всеми, кроме его автора. Переопределение операторов все же зло в плане читабельности кода, так как непонятно с чем ты имеешь дело, с переопределенным оператором или нативным.
0
В сочетании с ipython это просто шикарно для использования в качестве оболочки командной строки.
0
Ох, если нужно писать понятно, то лучше уж писать «четверостишиями» действительно длинные связки, тогда названиями переменных можно одновременно и код документировать.
0
Sign up to leave a comment.
Пайпы, the pythonic way