Pull to refresh

Comments 81

Да уж, всё гениальное просто. Но не всё простое гениально)
Утрированный пример на самом деле решается так:
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
Неправда ваша, было бы так:
range(100) | where(lambda x: x % 3 == 1) | add | my_function
Почему моя, если такой код в статье?
Действительно… в статье пример какой-то странный…
пример в статье высосан из пальца, признаюсь. Стоило порыться в рабочем коде и найти что-нибудь эдакое…
Пайпы читать проще и редактировать. Даже если они и длиннее выглядят. Поток данных проще: слева направо. А в Вашем примере надо сначала забежать глазами внутрь скобок, потом обратно.
total = sum(x for x in range(100) if x % 3 == 1)
result = my_function(total)

Читаемо? Даже очень. Лично я не люблю костыли. Пайпы это хорошо только в баше. И это НЕ pythonic way.
Ну, а теперь представьте, что нужно связать около 10-ка функций. Будет очень громоздко. А чем перегрузка операторов не pythonic way? Вроде же, это у них там стандартный механизм… Ну. Насколько мне известно…
pythonic — это не значит, что нужно писать, используя все средства языка для достижения цели. Pythonic — это как бы одновременно код должен быть простой, незагрязнëнный и понятный.
Ну. Это же не объективные понятия: простой, незагрязнённый и понятный. Кому-то pipe'ы понятнее и проще: зачем я должен придумывать имена временных переменных, если за меня это может сделать библиотека. Это проще, на мой вкус. И тут нет призыва использовать все средства. Используется одно из средств, удобным во многих ситуациях способов. И на самом деле, тут ничего нестандартного и страшного нет, потому что вот этот pipe — вполне себе чуть ограниченная воплощение стандартной концепции монады, которая считается хорошим инструментом программирования.
Ну, кроме того, в питоне не принято абьюзить синтаксис — это принято в хаскеле. Точно так же у меня сейчас в коде рабочего проекта есть реализация стрелок на питоне, которая использует >>. Мало кто выкупает, что и как там происходит. Я б не делал такого ради любви к людям.
Есть один момент — обычно, если есть около 10-ка функций идущих подряд, и это независимые функции, а не запрос, как в примере(select… where ...), то при отладке всегда нужно проверить результат каждого промежуточного вызова. И я, лично, вряд ли бы писал сначала по одному:
b=fun1(a)
c=fun2(b)
а в релизном варианте переписывал в виде с = fun1(a) | fun2()
Во-первых долго, во-вторых, мало ли, вдруг потом ещё придется мне, или кому-то другому, отлаживать программу.
Не надо ничего переписывать. Тестируйте каждый пайп в отдельности, а потом всю цепочку в совокупности. Привет юниттестам! В этом смысле никакой разницы с простыми функциями.

Это прикольно, но это не python way, это ruby way.
Чё-то это уже странные утверждения, imho. Если это написано на Python, то какой нафиг Ruby way? По-вашему, что? Вообще никакую библиотеку нельзя использовать, потому что там какие-то функции понаписаны, и никто не поймёт, что происходит, читая код, в котором они используются.

Мда… Фанаты — люди странные. Сделан классный механизм, стандартными средствами их любимого языка… Радоваться надо, разве нет? А то, что какие-то там проблемы могут быть, да… Могут. Но это повод библиотеку развивать, а не трясти перед ней python way'ем.

Эх :( Вы убили во мне желание воспользоваться в следующем проекте Python'ом. Какое-то у него Дао странное. Буду юзать Lua.
Ну какие фанаты, ну что это за разбрасывание лейбами? «Любители метапрограммирования — люди странные. Можно написать так просто, так нет же, дай сделать что-нибудь позапутаннее.»

Я вот сейчас поддерживаю большую довольно кодовую базу на питоне (80к+ SLoC, для питона это — очень много), и надо сказать, что любая попытка внести маленькие приятные штуки оказывается приятной только для автора. Или, другими словами, задолбало находить странные потуги чуваков на метапрограммирование. Хочется оторвать руки и пришить что-то, что будет выдавать поддерживаемый код, а не отрыжки мамонта.

Да, можно и с метапрограммированием писать поддерживаемый код. Вопрос лишь в самодисциплине, конечно. И за значения по умолчанию я принимаю то, что у большинства самодисциплина плоха.
Метапрограммирование? Это что такое? Декораторы? Метаклассы? Обычная весьма вещь, в любом коде встречается, множество фреймворков это использует.

Тут другой вопрос. Что есть хороший программист? Тот, который, вникая в систему и внося в нее изменения, не нарушает архитектуру и внутренние соглашения.

Что есть великолепный программист? Тот, кто может, усвоив правила большой системы, увеличить внутреннюю корректность, не нарушая цельности.

Что есть плохой программист? Тот, кто использует приемы, идущие в разрез с архитектурой системы; и не принимающий общие решения.

И метапрограммирование не имеет к этому никакого отношения.

Я бы сказал, что метапрограммирование — это построение DSL. Внутренних или внешних.
А декораторы-метаклассы — всего лишь детали реализации. Подмножество используемых для метапрограммирования инструментов.
Вероятно, это прозвучит забавно, но… Разве любое программирование API, уж тем более вблизи парадигмы ООП, — не то ли самое построение DSL?

Метапрограммирование в случае Питона — и не только его самого — это страшное название для простой вещи: изменения в поведении классов, будь то создание экземпляров класс, удаление, использование с операторами. Конечно, с такими вещами следует быть аккуратным. Но не более того!

И вообще буду строгим :). Признаться, после ознакомления с «DSL» на примере Scala, Haskell и Ruby, я перестал понимать, откуда взялась в специальной прессе и блогосфере такая шумиха вокруг этого неопределенного термина; так же посмеиваюсь над теми, кто делает идола из ООП-подхода. Подумаешь, догадались данные складывать в кучку с функциями для работы с ними! Полиморфизм — просто издержки жесткой системы типов; инкапсуляция — просто хорошая практика программирования; наследование — не самый удачный способ менять поведение объектов класса.

И все. :)
Лично у меня эти понятия различаются «в голове».
Можно делать ООП на С. А можно и на С++ писать «с классами», но без ООП.
DSL и ООП предполагают несколько отличающийся способ мышления.

Т.е. если я делаю внутренний DSL на Питоне — то использую метаклассы, декораторы, перегрузку операторов. Главное — думаю о получившейся конструкции как о создании нового микроязыка (не выходящего за синтаксис Питона, конечно). Вот и вся разница.
Но согласитесь, понятия различаются очень условно! Можно даже сказать, что DSL — это просто что-то вроде фразы «надо стремиться говорить на языке целевой области». А уж средства… ООП, функциональный подход, метапрограммирование, макросы (даже в Си/Си++ что-то похожее на DSL с их помощью вытворяют )… Кто во что горазд.

В рамках концепции ООП очень даже можно писать DSL. Скажем, тот же злосчастный джанговский ORM; многие другие реализации ActiveRecord — очень даже себе DSL.

Для меня твердой грани давно уже нет; видать слегка подпортил себе мышление обилием опробованных языков :-) DSL — это просто одна из задач, что иногда встречается.
Полностью согласен, различие небольшое.

О метапрограммировании на 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

Это не макросы, все гораздо хуже — шаблоны с частичной специализацией.
И это при том, что даже некий Александреску уже давно отказался от С++, как слишком проблемного языка :)
Хм. Особых проблем с отладкой pipe'ов в том же Bash нет. А вот эти Python-пайпы всяко более удобны.
Удобны более чем что? Чем непайпы?
В баше проблем нет, потому что нет и многого другого. Перегрузки операторов, например.
В bash как языке есть множество своих проблем: архаичный синтаксис, много избыточности и бла-бла-бла.

С другой стороны, в bash как инструменте для работы в командной строке есть много плюсов: простота, хорошая работа с текстом. Да и просто обмен данными через stdin/stdout, на мой взгляд — это блестяще. не говоря уже о десятилетиях добрых *никсовых традиций и лучших практик.

Однако, здесь речь идет прежде всего об использовании хорошо знакомого любым программистам синтаксиса для построения схожей абстракции на языке общего назначения…

Лично мне это интересно прежде всего в качестве элемента более крупной фреймворка или либы для поэтапной обработки данных, но никак не в качестве замены баша.
Да Bash тут только при том, что с отладкой проблемы не такие уж и большие возникают.

А с синтаксисом… Ну, я ещё повторюсь: человек хочет сделать стандартную математическую абстракцию — композицию функций. Хочет выразить её через оператор, чтобы не писать f.compose(f1).compose(f2).compose(f3)… * использовать не разумно, потому что она часто встречается в коде. А |, известно из Bash, вполне себе уживается с редко используемым битовым or. Всё, imho, рационально.

Вы же, наверное, не критикуете библиотеки за то, что в них перегружают арифметику для работы с матрицами или длинными числами. Или когда + используется для конкатенации строк, или когда то же | используется для записи грамматических правил.
Пытаюсь вспомнить, в каком коде я видел f.compose(f1).compose(f2).compose(f3)… *
Нет, не вспоминается.
Зато что-то вроде
f.compose(
    f1,
    f2,
    f3(arg)
)

писал не раз. Обратите внимание: композиция функций с использованием круглых скобок, таких привычных и родных.
Ай… Право же… Какой-то странный критерий оценки. Почему привычность должна быть приоритетнее удобства? Вероятно, нам с Вами друг друга просто не понять.
Нет. Ну в самом деле. А почему вы не настаиваете на том, чтобы вместо A * B + C писать: A.mul(B).add©? Привычнее же. Сплошные скобки… Прямо Lisp — классика.
Знаете, есть такая штука: принцип наименьшего удивления. Архитектурные решения, разработанные с использованием этого принципа, оказываются удобными. Так вот, использовать арифметические операторы для арифметических же действий — тоже привычно еще с детского садика.
Не чем 'непайпы', а как способ композиции функций. При чём тут перегрузка операторов — не очень понятно. Есть в математике такая стандартная операция — композиция. Философские причины того, почему в языке может быть перегружен оператор * для умножения матриц, а оператор | не может быть перегружен для композиции функций — лично для меня загадочны.
Перегружать — можно. Делайте на здоровье.

Используйте оператор | на полную катушку. Через год-два посчитайте, сколько раз за это время эта штука вам понадобилась. Перечитайте еще раз код, который эти пайпы использовал. И напишите success story, как все было хорошо и здорово, что вы уже не представляете себе жизни без чудо-механизма.
Не буду уже. Уже начал писать на Lua. А вы видели success story про умножение матриц при помощи *? Было бы интересно почитать, что там люди пишут.
Я читал много статей о numpy. Были среди них и success story.
Это действительно гениально, причем и как идея, так и реализация! Я в восторге!
Обязательно начну использовать у себя.
Не надо, пожалейте тех, кто будет работать с вами.
Никто не будет, я буду использовать в СВОИХ скриптах, которыми пользуюсь только я. Конечно же нельзя использовать pipe в проектах, код которых будет использовать кто-то ещё.
UFO just landed and posted this here
Если нельзя использовать в таких проектах, то это как бы звоночек. Может лучше вообще не использовать?
Главное — создать свой объект, т.к. в Питоне нельзя расширять предопределённые типы :(((. А трубы — это по практически то же, что и вызов методов:

[1,2,3,4] | where(lambda x: x<=2) | as_list

легко реализуется «питоническим» способом как:

KribleKrable([1,2,3,4]).where(lambda x: x<=2).as_list()
Есть неприятный нюанс. В каком namespace будет искать имена ваш KribleKrable?
Я имею в виду: как найдутся функции where и as_list из примера?
where и т.п. — методы объекта, возвращаемого функцией или конструктором KribleKrable. А сама KribleKrable ищется, очевидно, в том же пространстве имён, что и where из трубного примера.
«трубный» пример будет последовательно искать в локальном, глобальном и builtin пространствах имён.
Т.е. можно писать свои «трубы», оборачивая их декоратором @Pipe.
Наличие декоратора некрасиво, ну да ладно.

KribleKrable же должна явно содержать все доступные методы. Можно, конечно, выкрутиться.

1. Перегрузить __getattribute__ (или __getattr__ — не важно).
2. Хакнуть фрейм и вытащить f_globals/f_locals.
3. Эмулировать путь поиска имени.

Но это запутанная процедура, не делающая прочтение кода яснее — слишком логика отличается от привычной. Мне не нравится. Впрочем, «трубный» пример не нравится тоже.
Сомнительный запашок у получающегося кода.

Перегрузка операций (любых) — не самый лучший способ построения каскадов генераторов.
Из-за этой перегрузки в записи возможны досадные ошибки.

Понятность снижается (а должна бы увеличиваться).
Оператор | может встречаться и в привычном контексте битовых операций.
Здесь же придется внимательно смотреть: это 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.
Согласен с вашими доводами, конечно нужно знать, что делаешь и в серьезных проектах такое использовать нельзя.
Но это же красиво! Это «just for fan»! Почему бы и не поиспользовать это в своих скриптах? На мой взгляд, код многих мои скриптов с использованием pipe будет читаться и выглядеть гораздо проще и красивее, но это лишь мое мнение.
Если бы мы занимались увлекательным делом «напиши как можно хитрее» — тогда да, метод хорош.
Оно только выглядит проще — а понимается на самом деле сложнее чем запись «в лоб». Мы еще не переходили к обработке ошибок и чтению traceback — вот где поле непаханое.

И всё же повторюсь еще раз: предлагаемый подход ненадежен по построению.
Можно поиграть в занятную игру: придумывание кода, который валит библиотеку или заставляет ее работать «странно». Поверьте, этого кода много. У меня сразу нарисовалось несколько безобидных на первый взгляд вариантов. В примере я привел только самый короткий и очевидный.
imho, дело не в написании хитрее. Люди ищут способы писать красивее.

Вот я уже не помню когда арфметическое или видел в живом коде. Как-то битовые операции остались в моём далеком прошлом :)
И перегрузку или тоже не припоминаю.

Хотя, всё что вы сказали — да, не добавляет радости к этой красоте.

С другой стороны, питон не призван быть супернадежным инструментом, многие вещи построены на доверии и предположении, что разработчик понимает, что он делает. А если есть сомнения — сомнений быть не должно. Команда внутри себя может дjговориться, например, что в рабочем кода нельзя использовать pipe, и делов-то.
Множества, которые set/frozenset, тоже не используете?

> Команда внутри себя может дjговориться, например, что в рабочем кода нельзя использовать pipe, и делов-то.
Простите, потерял нить разговора. Вы предлагаете применять pipe исключительно в нерабочем коде?
множества так сходу не примопню, нужно разработчиков спросить :)

эх, мне казалась мысль очень цельной.
Ок, коротко: pipe вообще не факт, что для использования в реальных проектах.
Это может быть просто как иллюстрация того, что может язык (и насколько просто он это может). Как исследование в каком направлении языку двигаться.
В конце концов — есть люди у которых ipython в качестве шела :)
Кстати говоря, о множествах.

Вот если честь по чести, то эти самые множества используют тот же самый механизм переопределения __or__/__ror__; и ничего. Никто не кричит, что — О БОГИ! — программисты переопределили какую-то там побитовую операцию для каких-то там специальных типов.

Ну и что? В конце концов, реальный рабочий код пайп можно было бы разбавить нужными проверками и так далее.

Я все же утверждаю, что пайпы вполне работоспособны именно в качестве элемента какого-либо фреймворка, где можно твердо очертить область их применения.
Давайте поразмышляем, как сделать пайпы (с перегрузкой | и прочими плюшками) более безопасными в использовании.
«just for fan»? Для вентилятора, пожалуй, в самый раз.
Рекомендую к просмотру и осмыслению — Ларри Гастингс: The Python That Wasn't.
Соглашусь с вами. Лишние выкрутасы. :)
Эмс… Не понятно, а в каком месте пайпы могут перемешаться с битовым or? Если утверждается, что пайпы можно только с пайпами объединять или с генераторами. То есть, пайп будет между пайпов, а битовый or будет внутри каких-нибудь скобочек внутри lambda или ещё где… В каком месте они смешаются?
Может быть:
[1, 2, 3] | set([3, 4])
Пайпы хотелось бы объединять только с пайпами или генераторами.
А на деле запрета нет, и глюкавый код написать вполне можно.
Более того, такую проверку (забудем про тормоза на время) не очень-то и вставишь.
Чтобы все работало, нужно «генератор» заменить на «итерируемый объект» — иначе пример со списком работать не будет.
А итерируемость — слишком широкое понятие. Строка, например, подходит.
Если вспомнить, что оператор | переопределен в том числе и для множеств — проблема перестает быть академической.
Чудить можно с размахом.
Самый простой пример (он ничего не ломает, но слегка обескураживает):
from pipe import as_list
a = {1, 2, 3}
b = {4, 5}
print a | b | as_list
Только, на беду, содержит оператор __or__, приоритет которого выше чем у
__ror__. Нехорошо…

Опс. Нифига себе. Спасибо.
Кстати, вариант с KribleKrable этих недостатков лишён. А перегрузить класс и переопределить в нём часть функций можно всегда.
Вариант с KribleKrable ещё не засоряет namespace. Но в нём значительно больше пунктуации, читабельность сомнительна.
И всё же, если я правильно помню, всевозможные ORM делают именно через классы, безо всяческих syntactic sugar.

Ну, а в варианте с | слишком много переносов строк.
Это всё, конечно, интересно и красиво, но я бы долго моргал глазами, если бы увидел это в чужом коде.
И я уже молчу про проблемы, описанные Андреем: к чёрту этот блек-джек!
Можно было бы подумать о такой организации настоящих пайпов модуля subprocess, но нужно хорошо подумать…
Заметной разницы нету, кроме обращения порядка действий. В идеале хотелось бы что-то вроде $ в haskell — порядок остается тем же, а скобки писать не надо
по-моему, аналогичный эффект можно получить с помощью композиции функций. т.е. «идеалистический» код может выглядеть следующим образом:
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) # %) ?

Коллеги,

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

Комфортный синтаксис может быть необходимостью только в каком-то более широком контексте: фреймворке, бОльшей билиотеке, для каких-либо специальных задач.

Этот декоратор — proof of concept, не более того. Но красивый, тем и понравился :)
Хм…

Post.objects | where(user_id = 1) & where(is_published = True) | order('-time_published') | by_page(1, 10)

Забавно выходит :)
Вот именно как-то так я и вижу реальное применение подхода. Аккумулировать условия внутри пайпа, вычислять только по прямому требованию.

Только, в общем, джанговский ORM так и выглядит, только через точку; принципиальной разницы здесь нет.

Как насчет потока данных из сети? Скажем, такой синтаксис:

pipeline = socket | read_request | (callback & errback) | send_response
server.register(pipeline)
server.run(80)


здесь callback — сборка ответа на корректный запрос, errback — на некорректный; а оператор & — объединение корректного и некорректного обработчиков.
Ага :) А ещё:

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()

А это уже не так элегантно.
Поглядите на sqlalchemy. Там забавней — и строже одновременно.
Честно говоря мне не очень нравится sqlalchemy. И Джанговский ORM меня не до конца устраивает. Я вот уже месяц собираюсь написать свою надстройку над Джанговским ORM :)
Ваша статья натолкнула меня на некоторые мысли в контексте Ruby.

В нем проблемы с пайпами как таковой нет:

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

Отлично! :)

Итого: добрая половина дня убита занятием вот этой замечательной фигней ^.^
Вижу, разгорелся небольшой спор о том, что такое pythonic и что — нет.
Четкого определения нет и быть не может.
Мое чутье основывается на многолетнем чтении рабочей переписки разработчиков питона.
И на собственном опыте, конечно.
Иногда вроде бы красивая идея отвергается по результатам обсуждения.
Причины бывают разные.
Например, предлагаемый подход работает не в том духе, в котором сделано все остальное. Это может вводить пользователя в недоумение. В малых дозах — не страшно. В больших — становится натуральным ядом.
Случается и другое: работает в большинстве очевидных случаев, но в «темных углах» поведение непредсказуемо. Более того, правильное поведение неочевидно. Или мнения делятся «пополам-на-пополам».
Значит — решение плохое. Его можно применять в сторонних библиотеках — но в стандарт оно не войдет.
Бывает и третье. Все хорошо и понятно. Во мнениях сошлись. Но генерируемые ошибки (логичные и понятные) — неочевидны. Хороший спец понимает их с полуслова. «Простой программист» теряется в догадках. Это — тоже весомый минус. Предложение не будет принято.
Иногда ван Россум говорит: «Жопой чую, что-то здесь не так. Давайте отложим решение до лучших времен. И еще раз подумаем».

Это — строгие критерии отбора в core development team. Сторонние библиотеки не обязаны им следовать.

Более того. Уверен, что я перечислил далеко не все случаи — просто вспомнил о наиболее очевидных.

Что такое pythonic или unpythonic каждый решает сам для себя. И, тем не менее, я стараюсь следовать «чувству правильного», которое крайне неочевидно. Ориентир — тот дух, что пропитывает решения создателей языка.
Не одна моя поделка при внимательном рассмотрении оказалась — unpythonic. Красивая и лаконичная запись — и затем море проблем. Что-то улучшалось, гораздо большее — выбрасывалось. Жаль, но так лучше…
Брутальный образ Гвидо вырисовывается — мол, жопой чую %)
Предлагаю первую букву BDFL развёртывать в brutal.
Интересный подход. Но такие вещи все же надо делать на уровне языка, тогда это было бы вообще замечательно, а так это усложняет отладку кода и понимание кода всеми, кроме его автора. Переопределение операторов все же зло в плане читабельности кода, так как непонятно с чем ты имеешь дело, с переопределенным оператором или нативным.
В сочетании с ipython это просто шикарно для использования в качестве оболочки командной строки.
Забрел тут в обсуждение такого рода пайпов пару месяцев случайно… А ведь действительно удобней получится. В живых интерпретаторах с CLI это действительно удобней и наглядней. Попробовать, что ли, написать что-нибудь..?

Обработку картинок?
Ох, если нужно писать понятно, то лучше уж писать «четверостишиями» действительно длинные связки, тогда названиями переменных можно одновременно и код документировать.
Sign up to leave a comment.

Articles