Комментарии 42
А вот свой авторский материал я считаю интересным, вот и хотел им поделиться, и надо сказать, что некоторым он нравится.
если учебник пишите — то пишите его правильно. не надо у в учебнике писать плохой код, и учить программировать криво. есть 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, и что оно выбивается из общемировой парадигмы. Для хорошей эволюции требуется хорошее потрясение. Если я их потряс, то я справился), но получил ответку)))
Часто это крайне неудобно и нужны полноценные списки.
Сударь, из какого языка вы пришли? Генераторы (вероятно это что, что имеется ввиду) — это ленивый подход, и он предпочтителен в ФП. Да даже в технически императивном 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) — опечатка?
Всё, что было позже на русском языке, обычно сводится к упоминанию map, filter, reduce без контекста, почему именно они выступают примером ФП.
Надо отдать должное, ваше изложение (или оригинал, если это перевод) отличается связанностью, есть преамбула о самой парадигме ФП и вы продвинулись даже до замыкания и карринга.
Но данный материал не решает основной проблемы — как и зачем этим пользоваться?
И это проблема «синтетических» примеров. Кто немного в теме, тому материал не очень интересен, и так читано перечитано. Новичок, мало чего поймёт, кроме фишек в виде list comprehension, которые он и так скорее всего знает безотносительно ФП.
Кстати, обратите внимание на преимуществе конвейера в отладке кода — все оченнннно получается модульно.
И еще, этот материал не переводной, авторский)
Что касается list comprehension, то в английском питоновском языке еще есть dict comprehension и set comprehension, поэтому держитесь подальше от «спискового включения» ибо близоруко, т.к. dict и set по русски атрибутивно у вас не получится. Лучше говорить включение в список, в словарь, в множество
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 знать нужно, но применять — только четко понимая, что ты делаешь и к каким последствиям это приведет.
Зачем этой статье метка F#, если в статье ни слова об этом? Поставил минус — это вероятно у меня единственный способ хоть как-то повлиять на спам меток в статьях у подобных хайпожоров.
Поясняю, я указал две дополнительные метки: F# и clojure, чтобы привлечь внимание ф-программистов, в ситуации когда материалов по ФП и так мало, в надежде, что кто-то из смежных областей заинтересуется. Читайте внимательнее заголовки…
По поводу минуса. Это все претензии? А по существу темы будут? Мне поставили двойку по «матану», потому что я пришел без пиджака ;))
Спасибо, но я тщательно выбрал какие метки я хочу читать, а все остальные "я хочу привлечь из смежных областей" расцениваю как хайп. Мне не нужны другие метки и меня вполне устраивает пяток статей по теме вместо сотен ненужных мне статей. Вы не цените моё время — я ставлю минус вашей статье. Других претензий у меня нет, я не планирую даже вчитываться и терять своё время на чтение ненужных мне материалов. Пусть статью по существу оценивают участники из профильных меток, я же просто голосую ногами против спама меток. Не надо за меня решать, какие бы ещё непрофильные статьи мне воткнуть в голову и отобрать время — тут и так хватает рекламных статей, которым просто откровенно наплевать на тематичность — но если вы как программист считаете, что вам с ними по пути — ну так и получайте минусы. Если вы почитаете мои предыдущие комментарии — я вполне последователен в своём мнении, это не первый комментарий подобного рода. Не хотите? Жалко времени?
Ну так вот из-за таких как вы мне приходится после чтения заголовка делать уточнение, есть ли в тексте хоть одно упоминание F# — и если его нет, то уж извините. У меня свои принципы, я в карму не ставлю минусов, но уж если мне кажется, что кто-то планировал отнять моё время — пусть разменивается на минус. В конце концов, в предыдущей статье вы привели пример на F# — и я не стал минус ставить.
Хотите привлечь внимание из смежных меток — ну здорово! Придумывайте интересные сравнения нескольких языков, но блин, когда вообще ни одного упоминания — имейте совесть!
Можете продолжать навешивать ярлыки: вам факты — вы ярлык, вам идею, как улучшить статьи — вы ярлык, вам по делу — вы ярлык. Не вижу смысла продолжать разговор с человеком который развешивает на окружающих ярлык "придурки", "уроды" и подобное.
Спасибо за статью: всё чётко и по полочкам.
ПыСы: комплексующие комментаторы с синдромом Бога доставили, конечно. Воистину, зря закон о психиатрии советский поменяли.
Замечательная статья. Наткнулся на нее в поиске аналогов стрелочных функций и map из JS в Python и заодно узнал о наличии почти полноценной реализации возможности ФП в нем. Посчитал важным напомнить приверженцам пуристского следования стайлгайдов и исключительно императивного подхода, что несмотря на непопулярность ФП, отдельные элементы ФП широко используются в популярных императивных языках, особенно в JS и в веб-разработке в целом.
Кому-то может показаться, что нарушение стайлгайдов типа PEP8 (а это не единственный стайлгайд Python) порождают объективно ужасный код даже при широком применении Python в самых разных задачах. Получается тогда, что большая часть кода на JS и Scala (а она создавалась как более удобная замена Java), да и вообще из проектов команд, использующих новейшие средства условной «интернет-разработки», является нечитаемым мусором? Как бы не так! Почему вполне популярная парадигма не может использоваться в языке общего назначения, коим и является Python? Тем более необходимые возможности в нем реализованы.
«Императивное» мышление далеко не всегда уместно при создании, например, веб-приложений, где код выполняется асинхронно, а постоянная смена состояний сильно понижает эффективность тестирования, когда это жизненно необходимо. Также «мутабельность» неизбежно понижает отказоустойчивость сложной системы, особенно если потребуется вносить обновления без остановки ее работы.
Так, соблюдение стайлгайдов никоим образом не может стоять выше нахождения наиболее оптимального решения конкретной задачи. А их нарушение далеко не всегда ухудшает читаемость кода, ради чего эти стайлгайды и затевались. Хотя, может, не всем разработчикам на Python легко смириться с тем, что, например, в веб-разработке у отдельных компаний и команд может быть вообще свой стайлгайд
Основы функционального программирования на Python