Как стать автором
Обновить

Комментарии 42

НЛО прилетело и опубликовало эту надпись здесь
Применение def-функций в некоторых случаях не работает в стиле ФП.
и вот этому «ужасному» и «нарушающему» в книжке учат детей. автор что и в соседней статье про пайтон а там он честно признался что публикует главы своей книжки тут в качестве бесплатной рекламы
«в качестве бесплатной рекламы» — это ваши домыслы.

А вот свой авторский материал я считаю интересным, вот и хотел им поделиться, и надо сказать, что некоторым он нравится.
нравится или нет — это вопрос личного отношения. оценка "-1" к статье показывает что пока не понравилось в среднем большему количеству людей.

если учебник пишите — то пишите его правильно. не надо у в учебнике писать плохой код, и учить программировать криво. есть PEP8 — придерживайтесь его хотя бы. простые правила как чистить зубы по утрам. можно и не чистить, но будет вонять изо рта. в принципе вам плевать, жевать то есть чем пока что, но другие будут кривиться когда вы будете открывать рот

учебник должен учить правильному. если вам хочется творческой самореализации — то придумайте свои примеры и задачи. но синтаксис это святое и часть языка, а вы именно в него свои творческие видения запускаете. да PEP8 это формально свод рекомендаций, а не жестких правил, но именно это позволяет потом другим людям читать код и не плеваться в сторону автора. и так полно говнокода кругом, а вы еще учебник пишете о том как его сделать больше
PEP8 — это рекомендация, НЕ догма. ФП — это парадигма, которая, кстати, поддерживается языком. Если вы не хотите заниматься ФП, то вам не сюда.

Я добавил в материал книги дополнение, которое кому-то поможет взглянуть на синтаксис и возможности по-другому. По меньшей мере, ответить на вопрос, почему в языке есть функции map, reduce и filter, и как это связано с ФП.

Не стоит предписывать мне, что мне делать. Я — свободный человек, как и вы, надеюсь.

Вы выступаете тут, как окончательный арбитр. Не кажется ли вам, что такая роль — ущербна?

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

Скажите, а причем тут отладчик, если речь об исполнимом коде, т.е. причем тут время отладки, когда речь о времени исполнения?

неверно выразился, не отладчик, а для отладки,
правило же чисто декоративное — вывод имени функции в трассировке

В питоне у функциональных подходов очень много минусов и поэтому почти никто в питоне их не применяет.


Я сам пришёл в Питон с языка где функциональные подходы были естественными и первое что начал — применять их в Питоне. И затем спустя какое-то время… отказался от этого.


Вместо фильтров, мапов, теперь использую генераторы [ x for x in bla if foo], {x: y for x, y in bla} итп


Проблемы:


  • Выравнивание кода отступами.

Питон код выравнивает отступами. При этом, как правило, полиси каждого проекта ограничивает максимальную длину строки.
А функциональный подход занимает очень много символов.


Как правило запись генератора куда компактнее каскада map-filter-lambda


  • у питона нет лямбда функций. Только лямбда-оператор. То есть простую функцию из двух-пяти строк в лямбде не описать. Да и выравнивание тут снова мешает. То есть если в другом языке например для сортировки вполне применяли лямбду, то тут — нет


  • функциональные функции (сори за тафталогию) часто возвращают итераторы. Часто это крайне неудобно и нужны полноценные списки. Приходится оборачивать их в list и это получается громоздко и генератор выигрывает:



>>> a = [1,2,3]
>>> filter(lambda x: x > 2, a)
<filter object at 0x7f821f693b00>
>>> list(filter(lambda x: x > 2, a))
[3]
>>> [x for x in a if x > 2]
[3]

И так далее. В общем если заглядывать в реальные проекты на Python, то увидим, что функциональщина если и применяется то очень редко.

Но почему же редко. Все зависит. Как вам такое, например? Обратите внимание на конвейеры (про них в предыдущем посте)

# Множественная диспетчеризация (мультиметод)

import pandas as pd

class Data:
    uk, uk_scrbd, ru = range(3)
    
def load_data(identity):
    '''имплементация мультиметода на Python; загружает
    данные в зависимости от значения идентификатора'''
    return {
        Data.uk: lambda: do('ch01/UK2010.xls', 
                            pd.read_excel 
                           ),
        Data.ru: lambda: do('ch01/UK2010.xls', 
                            pd.read_excel, 
                            lambda o: o[o['Election Year'].notnull()]
                           )
    }[identity]()

load_data(Data.uk)

самописный конвейер может быть хорош, почему бы и нет. Хотя бы потому что он возвращает значение.


а вот map(filter плох итератором (выше я говорил)


PS: у Вас неплохие статьи, непонятно почему в минусах

Благодарю за оценку))
Дело в том, что в первых двух постах я поднял важную тему отсутствия правильного названия для ML, и что оно выбивается из общемировой парадигмы. Для хорошей эволюции требуется хорошее потрясение. Если я их потряс, то я справился), но получил ответку)))
В минусах — за оформление кода. Прошлые варианты были просто вырвиглаз. Я два дня подождал, пока читать начал, хотя поначалу пытался в IDE копировать и сам руками форматировать.
Ушло немного времени, чтобы освоиться.

Можно было просто подсказать. Это и называется отсутствием сотрудничества.
Часто это крайне неудобно и нужны полноценные списки.

Сударь, из какого языка вы пришли? Генераторы (вероятно это что, что имеется ввиду) — это ленивый подход, и он предпочтителен в ФП. Да даже в технически императивном C# подобные функции возвращают итерируемые ленивые объекты, а вовсе не окончательный массив или список. А вы хотите, чтобы за вас мапы и фильтры создавали новый список?

эм, какая разница откуда я пришёл, если именно в питоне часто нужны полноценные списки?


часто функция (в т.ч. библиотечная) принимает список, а не итератор.


а пришел я из perl


А вы хотите, чтобы за вас мапы и фильтры создавали новый список?

ага
причем если собирают ковейер из map,filter, итп, то пусть бы генерировал
а в итоге должен быть список

часто функция (в т.ч. библиотечная) принимает список, а не итератор.

Это очень странно, обычно принимают генераторы. Но если это так — то есть на то причины, и это никак не проблема map/filter/и т д!


ага
причем если собирают ковейер из map,filter, итп, то пусть бы генерировал
а в итоге должен быть список

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


def naturals():
    i = 0
    while (True):
        i += 1
        yield i

# Вот здесь твоя идея дает бесконечный список и все умирает.
even_naturals = filter(lambda x: x % 2 == 0, naturals())

for n in even_naturals:
    print(n)
    if n > 5:
        break

Но попробуй запустить этот код в питоне, и он отработает так, как ожидается.

Но если это так — то есть на то причины, и это никак не проблема map/filter/и т д!

не проблема map/filter, но map/filter становится обязательным применять совместно с list


def naturals():

только в реальной жизни программировать генератор натуральных чисел нафиг не надо


Вот здесь твоя идея дает бесконечный список и все умирает

я понимаю зачем нужны генераторы/итераторы
только в жизни гораздо чаще нужен список, нежели итератор.


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


поэтому в библиотеках списки файлов, хостов итп как правило принимаются диктами/листами а не итераторами

Вот в перле и программируй свою бухгалтерию, и файлы свои забери.) Мы здесь о высоких материях.)

так бухгалтерию или склад гораздо сложнее запрограммировать, нежели ряд чисел

next(filter) — опечатка?

Это ошибка. Материал писался 3 года назад. Все работало.
Должно быть так:
>>> filtered = iter(filter(lambda x: x % 2 == 0, seq))

А потом вот так:
>>> next(filtered)
2
>>> next(filtered)
4

Благодарю, что подметили. Текст исправлен.
Нормальное и практически полное описание «ФП на питоне» было довольно давно сделано в «междельмашевских» материалах.

Всё, что было позже на русском языке, обычно сводится к упоминанию map, filter, reduce без контекста, почему именно они выступают примером ФП.

Надо отдать должное, ваше изложение (или оригинал, если это перевод) отличается связанностью, есть преамбула о самой парадигме ФП и вы продвинулись даже до замыкания и карринга.

Но данный материал не решает основной проблемы — как и зачем этим пользоваться?
И это проблема «синтетических» примеров. Кто немного в теме, тому материал не очень интересен, и так читано перечитано. Новичок, мало чего поймёт, кроме фишек в виде list comprehension, которые он и так скорее всего знает безотносительно ФП.
Вы смотрели пост про функциональный конвейер? Он представляет собой прекрасный пример практического применения ФП на практике. При доработке он может содержать ветвления — все в функциональном стиле. Помимо этого, у меня есть примеры имплементаций моделей ML в виде конвейеров, простенькой экспертной системы и прочие вкусности типа графического интерфейса)) Хотел было продолжить серию. Будет время сделаю.

Кстати, обратите внимание на преимуществе конвейера в отладке кода — все оченнннно получается модульно.

И еще, этот материал не переводной, авторский)

Что касается list comprehension, то в английском питоновском языке еще есть dict comprehension и set comprehension, поэтому держитесь подальше от «спискового включения» ибо близоруко, т.к. dict и set по русски атрибутивно у вас не получится. Лучше говорить включение в список, в словарь, в множество

А зачем вы добавили этот и предыдущий посты в хабы F# и Clojure? Из-за одного примера конвейера на F#? Думаю, не стоит засорять хабы материалом, не имеющим к их темам отношения.

Резонно. Но темы коррелированы, а людей в ФП мало.
Python крут! Он дает кучу возможностей отстрелить себе ногу почесав ухо. Так что функциональные фичи Python нужно применять с осторожностью. Примерно на уровне регулярных выражений. Когда нет альтернативы и когда альтернатива требует кучи кода, да и пожалуй все. Работая с Python надо помнить про duck typing. Даже такой кривой итератор как в примере ниже, работает и с map, и с filter, и с reduce.
from functools import reduce
from random import randrange


class MyIterator():

    def __iter__(self):
        return self

    def __next__(self):
        result = randrange(10)
        print("Generated:", result)
        if result == 0:
            raise StopIteration()
        return result


if __name__ == '__main__':

    my_iterator = MyIterator()

    print("Map example call:", list(map(lambda x: x, my_iterator)))
    print("Filter example call:", list(filter(lambda x: x % 2 == 0, my_iterator)))
    print("Reduce example call:", reduce(lambda acc, x: acc + x, my_iterator, 0))

Правда, добавляет перчинки:
...
    seq = (1,2,3,4,5,6,7,8,9)
    mapped_static = map(lambda x: x, seq)
    print("First call of mapped static:", list(mapped_static))
    print("Second call of mapped static:", list(mapped_static))

    my_iterator = MyIterator()
    mapped_dynamic = map(lambda x: x, my_iterator)
    print("First call of mapped dynamic:", list(mapped_dynamic))
    print("Second call of mapped dynamic:", list(mapped_dynamic))

Результат будет вроде такого:
...
First call of mapped static: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Second call of mapped static: []
Generated: 2
Generated: 6
Generated: 6
Generated: 9
Generated: 8
Generated: 2
Generated: 6
Generated: 5
Generated: 7
Generated: 5
Generated: 4
Generated: 4
Generated: 0
First call of mapped dynamic: [2, 6, 6, 9, 8, 2, 6, 5, 7, 5, 4, 4]
Generated: 6
Generated: 5
Generated: 1
Generated: 1
Generated: 6
Generated: 8
Generated: 0
Second call of mapped dynamic: [6, 5, 1, 1, 6, 8]

Не то чтобы ужас-ужас, но неприятно.

А уж что там на самом деле за объект в более-менее сложном проекте — это вопрос на миллион. Иногда смотришь на код — вроде обычный for item in some_list:, но присмотревшись к этому some_list понимаешь, что там внутри нифига не список. А нечто навороченное, читающее данные из очереди сообщений в дополнительных потоках, с вычиткой данных наперед и буферизацией в памяти. Ну или какой-нибудь словарь с конфигом. Присматриваешься — а там Содом с Гоморрой с получением данных из переменных окружения, локальных конфигурационных файлов и базы данных. Все это смешивается, но не взбалтывается и время от времени перечитывается. Но при этом по большей части снаружи выглядит как обычный дикт. А меньшая часть — иногда стреляет, даря незабываемые ощущения батхерта на вроде бы ровном месте.
Впрочем, проблем может доставить даже изменение типа с тапла на лист.

Так что функциональные возможности Python знать нужно, но применять — только четко понимая, что ты делаешь и к каким последствиям это приведет.
Как известно, из любой конфетки можно сделать «не то». )
Однако, пример — показателен.
Вот очень неплохая библиотека github.com/kachayev/fn.py с недостающими структурами данных и функциями, рекомендую.
Благодарю;)

Зачем этой статье метка F#, если в статье ни слова об этом? Поставил минус — это вероятно у меня единственный способ хоть как-то повлиять на спам меток в статьях у подобных хайпожоров.

На портале «хабр» у меня зарегистрирована моя страница. Вы являетесь на мою страницу и прилюдно начинаете обзываться. Так кто тут «хайпожер» тогда?

Поясняю, я указал две дополнительные метки: F# и clojure, чтобы привлечь внимание ф-программистов, в ситуации когда материалов по ФП и так мало, в надежде, что кто-то из смежных областей заинтересуется. Читайте внимательнее заголовки…

По поводу минуса. Это все претензии? А по существу темы будут? Мне поставили двойку по «матану», потому что я пришел без пиджака ;))

Спасибо, но я тщательно выбрал какие метки я хочу читать, а все остальные "я хочу привлечь из смежных областей" расцениваю как хайп. Мне не нужны другие метки и меня вполне устраивает пяток статей по теме вместо сотен ненужных мне статей. Вы не цените моё время — я ставлю минус вашей статье. Других претензий у меня нет, я не планирую даже вчитываться и терять своё время на чтение ненужных мне материалов. Пусть статью по существу оценивают участники из профильных меток, я же просто голосую ногами против спама меток. Не надо за меня решать, какие бы ещё непрофильные статьи мне воткнуть в голову и отобрать время — тут и так хватает рекламных статей, которым просто откровенно наплевать на тематичность — но если вы как программист считаете, что вам с ними по пути — ну так и получайте минусы. Если вы почитаете мои предыдущие комментарии — я вполне последователен в своём мнении, это не первый комментарий подобного рода. Не хотите? Жалко времени?


Ну так вот из-за таких как вы мне приходится после чтения заголовка делать уточнение, есть ли в тексте хоть одно упоминание F# — и если его нет, то уж извините. У меня свои принципы, я в карму не ставлю минусов, но уж если мне кажется, что кто-то планировал отнять моё время — пусть разменивается на минус. В конце концов, в предыдущей статье вы привели пример на F# — и я не стал минус ставить.


Хотите привлечь внимание из смежных меток — ну здорово! Придумывайте интересные сравнения нескольких языков, но блин, когда вообще ни одного упоминания — имейте совесть!

Самодовольство, напыщенность, надменность, высокомерие, «не читал, но осуждаю» — уникальная коллекция комплексов. Какой замечательный экземпляр для психоаналитика…

Можете продолжать навешивать ярлыки: вам факты — вы ярлык, вам идею, как улучшить статьи — вы ярлык, вам по делу — вы ярлык. Не вижу смысла продолжать разговор с человеком который развешивает на окружающих ярлык "придурки", "уроды" и подобное.

Ни разу никого не обзывал и не собираюсь. Более того, ни разу никого не унижал и тоже не собираюсь. И не следует все ставить с ног на голову. Подумайте лучше о правилах приличного поведения в научпоп сообществе, прежде чем писать комменты.

Спасибо за статью: всё чётко и по полочкам.

ПыСы: комплексующие комментаторы с синдромом Бога доставили, конечно. Воистину, зря закон о психиатрии советский поменяли.

Благодарю за человеческое отношение)). Писать научпоп статьи - дело не из приятных...)

Замечательная статья. Наткнулся на нее в поиске аналогов стрелочных функций и map из JS в Python и заодно узнал о наличии почти полноценной реализации возможности ФП в нем. Посчитал важным напомнить приверженцам пуристского следования стайлгайдов и исключительно императивного подхода, что несмотря на непопулярность ФП, отдельные элементы ФП широко используются в популярных императивных языках, особенно в JS и в веб-разработке в целом.

Кому-то может показаться, что нарушение стайлгайдов типа PEP8 (а это не единственный стайлгайд Python) порождают объективно ужасный код даже при широком применении Python в самых разных задачах. Получается тогда, что большая часть кода на JS и Scala (а она создавалась как более удобная замена Java), да и вообще из проектов команд, использующих новейшие средства условной «интернет-разработки», является нечитаемым мусором? Как бы не так! Почему вполне популярная парадигма не может использоваться в языке общего назначения, коим и является Python? Тем более необходимые возможности в нем реализованы.

«Императивное» мышление далеко не всегда уместно при создании, например, веб-приложений, где код выполняется асинхронно, а постоянная смена состояний сильно понижает эффективность тестирования, когда это жизненно необходимо. Также «мутабельность» неизбежно понижает отказоустойчивость сложной системы, особенно если потребуется вносить обновления без остановки ее работы.

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

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

Публикации

Истории