Документация Python неофициальный перевод

functional.md

987 строк · 78.7 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.0/howto/functional.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Руководство по функциональному программированию89| Автор: | A. M. Kuchling |10| --- | --- |11| Версия: | 0.31 |1213(Это первый черновик. Пожалуйста, присылайте комментарии/сообщения об ошибках/предложения на [amk@amk.ca](https://python-all.ru/3.0/howto/functional.html).)1415В этом документе мы рассмотрим возможности Python, подходящие для реализации программ в функциональном стиле. После введения в концепции функционального программирования мы рассмотрим такие языковые средства, как [*итераторы*](https://python-all.ru/3.0/glossary.html#term-iterator) и [*генераторы*](https://python-all.ru/3.0/glossary.html#term-generator), а также соответствующие модули библиотеки, например [`itertools`](https://python-all.ru/3.0/library/itertools.html#module-itertools) и [`functools`](https://python-all.ru/3.0/library/functools.html#module-functools).1617## Введение1819В этом разделе объясняется основная концепция функционального программирования; если требуется изучить только возможности языка Python, можно пропустить его и перейти к следующему разделу.2021Языки программирования поддерживают декомпозицию задач несколькими различными способами:2223- Большинство языков программирования являются **процедурными**: программы представляют собой списки инструкций, которые указывают компьютеру, что делать с входными данными программы. C, Pascal и даже оболочки Unix являются процедурными языками.24- В **декларативных** языках пишется спецификация, описывающая решаемую задачу, а реализация языка самостоятельно определяет, как эффективно выполнить вычисления. SQL – это декларативный язык, с которым вы, скорее всего, знакомы; SQL-запрос описывает набор данных, который вы хотите получить, а механизм SQL решает, сканировать таблицы или использовать индексы, какие подзапросы выполнять в первую очередь и т.д.25- **Объектно-ориентированные** программы манипулируют коллекциями объектов. Объекты имеют внутреннее состояние и поддерживают методы, которые запрашивают или изменяют это внутреннее состояние определённым образом. Smalltalk и Java являются объектно-ориентированными языками. C++ и Python – языки, поддерживающие объектно-ориентированное программирование, но не навязывающие использование объектно-ориентированных возможностей.26- **Функциональное** программирование разбивает задачу на набор функций. В идеале функции только принимают входные данные и выдают результаты, не имея внутреннего состояния, влияющего на вывод для заданного входа. К известным функциональным языкам относятся семейство ML (Standard ML, OCaml и другие варианты) и Haskell.2728Разработчики некоторых языков программирования решают сделать упор на один конкретный подход к программированию. Это часто затрудняет написание программ, использующих другой подход. Другие языки являются мультипарадигмальными и поддерживают несколько различных подходов. Lisp, C++ и Python – мультипарадигмальные; в каждом из этих языков можно писать программы или библиотеки, преимущественно процедурные, объектно-ориентированные или функциональные. В большой программе разные разделы могут быть написаны с использованием разных подходов; например, графический интерфейс может быть объектно-ориентированным, а логика обработки – процедурной или функциональной.2930В функциональной программе входные данные проходят через набор функций. Каждая функция обрабатывает свой вход и выдаёт некоторый результат. Функциональный стиль не одобряет функции с побочными эффектами, изменяющими внутреннее состояние или вносящими другие изменения, невидимые в возвращаемом значении функции. Функции, не имеющие побочных эффектов вообще, называются **чисто функциональными**. Избегание побочных эффектов означает отказ от использования структур данных, которые обновляются в процессе выполнения программы; результат каждой функции должен зависеть только от её входных данных.3132Некоторые языки очень строги в отношении чистоты и даже не имеют операторов присваивания, таких как `a=3` или `c = a + b`, но избежать всех побочных эффектов сложно. Например, вывод на экран или запись в файл – это побочные эффекты. В Python вызов [`print()`](https://python-all.ru/3.0/library/functions.html#print) или [`time.sleep()`](https://python-all.ru/3.0/library/time.html#time.sleep) не возвращает полезного значения; эти функции вызываются только ради побочных эффектов: отправить текст на экран или приостановить выполнение на секунду.3334Программы на Python, написанные в функциональном стиле, обычно не доходят до крайности полного отказа от ввода-вывода или присваиваний; вместо этого они предоставляют интерфейс, выглядящий функциональным, но внутри используют не функциональные возможности. Например, реализация функции всё равно будет использовать присваивания локальным переменным, но не будет изменять глобальные переменные или иметь другие побочные эффекты.3536Функциональное программирование можно считать противоположностью объектно-ориентированного. Объекты – это небольшие капсулы, содержащие некоторое внутреннее состояние вместе с набором вызовов методов, позволяющих изменять это состояние, а программы состоят из выполнения правильного набора изменений состояния. Функциональное программирование стремится максимально избегать изменений состояния и работает с данными, передаваемыми между функциями. В Python вы можете комбинировать эти два подхода, написав функции, которые принимают и возвращают экземпляры, представляющие объекты в вашем приложении (сообщения электронной почты, транзакции и т.д.).3738Функциональный дизайн может показаться странным ограничением для работы. Зачем избегать объектов и побочных эффектов? У функционального стиля есть теоретические и практические преимущества:3940- Формальная доказуемость.41- Модульность.42- Композируемость.43- Лёгкость отладки и тестирования.4445### Формальная доказуемость4647Теоретическое преимущество заключается в том, что легче построить математическое доказательство корректности функциональной программы.4849Уже долгое время исследователи заинтересованы в поиске способов математического доказательства корректности программ. Это отличается от тестирования программы на множестве входных данных и заключения, что её вывод обычно корректен, или чтения исходного кода программы и заключения, что код выглядит правильно; цель – строгое доказательство того, что программа выдаёт правильный результат для всех возможных входных данных.5051Метод, используемый для доказательства корректности программ, заключается в записи **инвариантов** – свойств входных данных и переменных программы, которые всегда истинны. Для каждой строки кода затем показывается, что если инварианты X и Y истинны **до** выполнения строки, то слегка изменённые инварианты X’ и Y’ истинны **после** выполнения строки. Это продолжается до конца программы, после чего инварианты должны соответствовать желаемым условиям на выход программы.5253Избегание присваиваний в функциональном программировании возникло потому, что присваивания сложно обрабатывать с помощью этого метода; присваивания могут нарушить инварианты, которые были истинны до присваивания, не порождая новых инвариантов, которые можно было бы распространить дальше.5455К сожалению, доказательство корректности программ в значительной степени непрактично и не применимо к программному обеспечению на Python. Даже тривиальные программы требуют доказательств длиной в несколько страниц; доказательство корректности для умеренно сложной программы было бы огромным, и мало какие (или вообще никакие) из программ, которые вы используете ежедневно (интерпретатор Python, ваш XML-парсер, веб-браузер), могли бы быть доказаны корректными. Даже если бы вы записали или сгенерировали доказательство, возник бы вопрос его проверки; возможно, в нём есть ошибка, и вы ошибочно полагаете, что доказали корректность программы.5657### Модульность5859Более практическое преимущество функционального программирования заключается в том, что оно заставляет разбивать задачу на небольшие части. Программы становятся более модульными. Проще определить и написать маленькую функцию, которая делает одно дело, чем большую функцию, выполняющую сложное преобразование. Маленькие функции также легче читать и проверять на ошибки.6061### Лёгкость отладки и тестирования6263Тестировать и отлаживать программу в функциональном стиле проще.6465Отладка упрощается, потому что функции обычно малы и чётко определены. Когда программа не работает, каждая функция является точкой интерфейса, где можно проверить корректность данных. Можно просмотреть промежуточные входные и выходные данные, чтобы быстро изолировать функцию, ответственную за ошибку.6667Тестирование проще, потому что каждая функция является потенциальным объектом для модульного теста. Функции не зависят от состояния системы, которое нужно воспроизводить перед запуском теста; вместо этого нужно только синтезировать правильный вход и затем проверить, что выход соответствует ожиданиям.6869### Композируемость7071В процессе работы над программой в функциональном стиле вы напишете ряд функций с различными входными и выходными данными. Некоторые из этих функций будут неизбежно специализированы для конкретного приложения, но другие будут полезны в самых разных программах. Например, функция, принимающая путь к каталогу и возвращающая все XML-файлы в нём, или функция, принимающая имя файла и возвращающая его содержимое, могут применяться во многих различных ситуациях.7273Со временем у вас сформируется личная библиотека утилит. Часто вы будете собирать новые программы, компонуя существующие функции в новой конфигурации и написав несколько функций, специализированных для текущей задачи.7475## Итераторы7677Я начну с рассмотрения возможности языка Python, которая является важной основой для написания функциональных программ: итераторов.7879Итератор – это объект, представляющий поток данных; этот объект возвращает данные по одному элементу за раз. Итератор Python должен поддерживать метод `__next__()`, который не принимает аргументов и всегда возвращает следующий элемент потока. Если в потоке больше нет элементов, `__next__()` должен возбудить исключение `StopIteration`. Однако итераторы не обязательно должны быть конечными; написать итератор, порождающий бесконечный поток данных, вполне разумно.8081Встроенная функция [`iter()`](https://python-all.ru/3.0/library/functions.html#iter) принимает произвольный объект и пытается вернуть итератор, который будет возвращать содержимое или элементы объекта, возбуждая [`TypeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.TypeError), если объект не поддерживает итерацию. Некоторые встроенные типы данных Python поддерживают итерацию, наиболее распространённые – списки и словари. Объект называется **итерируемым**, если для него можно получить итератор.8283Можно поэкспериментировать с интерфейсом итерации вручную:8485```python86>>> L = [1,2,3]87>>> it = iter(L)88>>> it89<...iterator object at ...>90>>> it.__next__()91192>>> next(it)93294>>> next(it)95396>>> next(it)97Traceback (most recent call last):98  File "<stdin>", line 1, in ?99StopIteration100>>>101```102103Python ожидает итерируемые объекты в нескольких различных контекстах, важнейшим из которых является инструкция `for`. В инструкции `for X in Y`, Y должен быть итератором или объектом, для которого `iter()` может создать итератор. Следующие две инструкции эквивалентны:104105```python106for i in iter(obj):107    print(i)108109for i in obj:110    print(i)111```112113Итераторы можно материализовать в списки или кортежи с помощью функций-конструкторов [`list()`](https://python-all.ru/3.0/library/functions.html#list) или [`tuple()`](https://python-all.ru/3.0/library/functions.html#tuple):114115```python116>>> L = [1,2,3]117>>> iterator = iter(L)118>>> t = tuple(iterator)119>>> t120(1, 2, 3)121```122123Распаковка последовательностей также поддерживает итераторы: если известно, что итератор вернёт N элементов, их можно распаковать в N-кортеж:124125```python126>>> L = [1,2,3]127>>> iterator = iter(L)128>>> a,b,c = iterator129>>> a,b,c130(1, 2, 3)131```132133Встроенные функции, такие как [`max()`](https://python-all.ru/3.0/library/functions.html#max) и [`min()`](https://python-all.ru/3.0/library/functions.html#min), могут принимать единственный аргумент-итератор и возвращать наибольший или наименьший элемент. Операторы `"in"` и `"not in"` также поддерживают итераторы: `X in iterator` истинно, если X найден в потоке, возвращаемом итератором. С бесконечным итератором возникнут очевидные проблемы: `max()`, `min()` и `"not in"` никогда не завершатся, а если элемент X никогда не появится в потоке, оператор `"in"` тоже не вернёт результат.134135Обратите внимание: по итератору можно двигаться только вперёд; нет способа получить предыдущий элемент, сбросить итератор или создать его копию. Объекты-итераторы могут опционально предоставлять такие дополнительные возможности, но протокол итератора определяет только метод `next()`. Поэтому функции могут потребить весь вывод итератора, и если понадобится выполнить с этим же потоком что-то другое, придётся создать новый итератор.136137### Типы данных, поддерживающие итераторы138139Мы уже видели, как списки и кортежи поддерживают итераторы. На самом деле любой тип последовательности Python, например строки, автоматически поддерживает создание итератора.140141Вызов [`iter()`](https://python-all.ru/3.0/library/functions.html#iter) для словаря возвращает итератор, который будет перебирать ключи словаря:142143```python144>>> m = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6,145...      'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12}146>>> for key in m:147...     print(key, m[key])148Mar 3149Feb 2150Aug 8151Sep 9152Apr 4153Jun 6154Jul 7155Jan 1156May 5157Nov 11158Dec 12159Oct 10160```161162Порядок по существу случаен, так как он основан на хеш-упорядочении объектов в словаре.163164Применение [`iter()`](https://python-all.ru/3.0/library/functions.html#iter) к словарю всегда выполняет обход по ключам, но у словарей есть методы, возвращающие другие итераторы. Если требуется перебрать значения или пары ключ-значение, можно явно вызвать методы `values()` или `items()`, чтобы получить соответствующий итератор.165166Конструктор [`dict()`](https://python-all.ru/3.0/library/stdtypes.html#dict) может принимать итератор, возвращающий конечный поток кортежей вида `(key, value)`:167168```python169>>> L = [('Italy', 'Rome'), ('France', 'Paris'), ('US', 'Washington DC')]170>>> dict(iter(L))171{'Italy': 'Rome', 'US': 'Washington DC', 'France': 'Paris'}172```173174Файлы также поддерживают итерацию путём вызова метода `readline()`, пока в файле есть строки. Это означает, что можно прочитать каждую строку файла следующим образом:175176```python177for line in file:178    # сделать что-то для каждой строки179    ...180```181182Множества могут принимать своё содержимое из итерируемого объекта и позволяют перебирать элементы множества:183184```python185S = {2, 3, 5, 7, 11, 13}186for i in S:187    print(i)188```189190## Генераторные выражения и списковые включения191192Две распространённые операции над выводом итератора: 1) выполнение некоторого действия для каждого элемента, 2) выбор подмножества элементов, удовлетворяющих некоторому условию. Например, имея список строк, можно удалить завершающие пробелы из каждой строки или извлечь все строки, содержащие заданную подстроку.193194Списковые включения и генераторные выражения (сокращённо: “listcomps” и “genexps”) – это краткая запись таких операций, заимствованная из функционального языка программирования Haskell ([http://www.haskell.org](https://python-all.ru/3.0/howto/functional.html)). Можно удалить все пробелы из потока строк с помощью следующего кода:195196```python197line_list = ['  line 1\n', 'line 2  \n', ...]198199# Генераторное выражение – возвращает итератор200stripped_iter = (line.strip() for line in line_list)201202# Списочное выражение – возвращает список203stripped_list = [line.strip() for line in line_list]204```205206Можно отобрать только определённые элементы, добавив условие `"if"`:207208```python209stripped_list = [line.strip() for line in line_list210                 if line != ""]211```212213При использовании генератора списков получается список Python; `stripped_list` – это список, содержащий результирующие строки, а не итератор. Генераторные выражения возвращают итератор, который вычисляет значения по мере необходимости, не требуя материализации всех значений сразу. Это означает, что генераторы списков не подходят для работы с итераторами, возвращающими бесконечный поток или очень большой объём данных. В таких ситуациях предпочтительнее генераторные выражения.214215Генераторные выражения заключаются в круглые скобки («()»), а списковые включения – в квадратные скобки («\[\]»). Генераторные выражения имеют вид:216217```python218( expression for expr in sequence1219             if condition1220             for expr2 in sequence2221             if condition2222             for expr3 in sequence3 ...223             if condition3224             for exprN in sequenceN225             if conditionN )226```227228Опять же, для спискового включения отличаются только внешние скобки (квадратные вместо круглых).229230Элементы генерируемого вывода – это последовательные значения `выражения`. Предложения `if` необязательны; если они присутствуют, `выражение` вычисляется и добавляется к результату, только когда `условие` истинно.231232Генераторные выражения всегда должны быть записаны внутри круглых скобок, но скобки, обозначающие вызов функции, также подходят. Если нужно создать итератор, который будет немедленно передан функции, можно написать:233234```python235obj_total = sum(obj.count for obj in list_all_objects())236```237238Предложения `for...in` содержат последовательности, по которым производится итерация. Последовательности не обязаны быть одинаковой длины, поскольку итерация по ним выполняется слева направо, а **не** параллельно. Для каждого элемента из `sequence1` последовательность `sequence2` перебирается с начала. Затем `sequence3` перебирается для каждой результирующей пары элементов из `sequence1` и `sequence2`.239240Другими словами, списковое включение или генераторное выражение эквивалентно следующему коду Python:241242```python243for expr1 in sequence1:244    if not (condition1):245        continue   # Пропустить этот элемент246    for expr2 in sequence2:247        if not (condition2):248            continue    # Пропустить этот элемент249        ...250        for exprN in sequenceN:251             if not (conditionN):252                 continue   # Пропустить этот элемент253254             # Вывести значение255             # выражения.256```257258Это означает, что при наличии нескольких предложений `for...in` и отсутствии предложений `if` длина результирующего вывода будет равна произведению длин всех последовательностей. Если есть два списка длины 3, результирующий список будет содержать 9 элементов:259260```python261>>> seq1 = 'abc'262>>> seq2 = (1,2,3)263>>> [(x,y) for x in seq1 for y in seq2]264[('a', 1), ('a', 2), ('a', 3),265 ('b', 1), ('b', 2), ('b', 3),266 ('c', 1), ('c', 2), ('c', 3)]267```268269Чтобы не создавать неоднозначности в грамматике Python, если `выражение` создаёт кортеж, оно должно быть заключено в круглые скобки. Первый генератор списка ниже содержит синтаксическую ошибку, а второй – корректен:270271```python272# Синтаксическая ошибка273[ x,y for x in seq1 for y in seq2]274# Верно275[ (x,y) for x in seq1 for y in seq2]276```277278## Генераторы279280Генераторы – это особый класс функций, упрощающих написание итераторов. Обычные функции вычисляют значение и возвращают его, а генераторы возвращают итератор, который возвращает поток значений.281282Вы, несомненно, знакомы с тем, как работают обычные функции в Python или C. При вызове функции она получает приватное пространство имён, в котором создаются её локальные переменные. Когда функция доходит до оператора `return`, локальные переменные уничтожаются, а значение возвращается вызывающему коду. Последующий вызов той же функции создаёт новое приватное пространство имён и новый набор локальных переменных. Но что, если бы локальные переменные не уничтожались при выходе из функции? Что, если бы можно было возобновить выполнение функции с того места, где она остановилась? Именно это и обеспечивают генераторы; их можно рассматривать как возобновляемые функции.283284Вот простейший пример функции-генератора:285286```python287def generate_ints(N):288    for i in range(N):289        yield i290```291292Любая функция, содержащая ключевое слово `yield`, является генераторной функцией; это обнаруживается компилятором [*байткода*](https://python-all.ru/3.0/glossary.html#term-bytecode) Python, который в результате компилирует такую функцию особым образом.293294При вызове генераторной функции она не возвращает единственное значение; вместо этого она возвращает объект-генератор, поддерживающий протокол итератора. При выполнении выражения `yield` генератор выводит значение `i`, аналогично инструкции `return`. Большое различие между `yield` и инструкцией `return` состоит в том, что при достижении `yield` состояние выполнения генератора приостанавливается, а локальные переменные сохраняются. При следующем вызове метода `.__next__()` генератора функция возобновит выполнение.295296Вот пример использования генератора `generate_ints()`:297298```python299>>> gen = generate_ints(3)300>>> gen301<generator object at ...>302>>> next(gen)3030304>>> next(gen)3051306>>> next(gen)3072308>>> next(gen)309Traceback (most recent call last):310  File "stdin", line 1, in ?311  File "stdin", line 2, in generate_ints312StopIteration313```314315Можно также написать `for i in generate_ints(5)`, или `a,b,c = generate_ints(3)`.316317Внутри генераторной функции инструкция `return` может использоваться только без значения и сигнализирует об окончании последовательности значений; после выполнения `return` генератор не может возвращать дальнейшие значения. `return` со значением, например `return 5`, является синтаксической ошибкой внутри генераторной функции. Конец результатов генератора также может быть обозначен возбуждением `StopIteration` вручную или просто позволив выполнению дойти до конца функции.318319Эффект генераторов можно было бы воспроизвести вручную, написав собственный класс и сохранив все локальные переменные генератора как атрибуты экземпляра. Например, возврат списка целых чисел можно было бы реализовать, установив `self.count` в 0, а метод `__next__()` будет увеличивать `self.count` и возвращать его. Однако для сколько-нибудь сложного генератора написание соответствующего класса может оказаться гораздо более запутанным.320321Набор тестов, входящий в состав библиотеки Python, `test_generators.py`, содержит ряд более интересных примеров. Вот один генератор, который рекурсивно реализует симметричный обход дерева с помощью генераторов.322323```python324# Рекурсивный генератор, порождающий листья дерева в порядке in-order.325def inorder(t):326    if t:327        for x in inorder(t.left):328            yield x329330        yield t.label331332        for x in inorder(t.right):333            yield x334```335336Два других примера в файле `test_generators.py` содержат решения задачи о N ферзях (разместить N ферзей на доске NxN так, чтобы ни один ферзь не угрожал другому) и задачи о ходе коня (найти маршрут, по которому конь обойдёт все клетки доски NxN, не посещая ни одну клетку дважды).337338### Передача значений в генератор339340В Python 2.4 и ранее генераторы только выдавали результат. Как только код генератора вызывался для создания итератора, не было способа передать новую информацию в функцию при возобновлении её выполнения. Можно было сымитировать эту возможность, заставив генератор обращаться к глобальной переменной или передавая изменяемый объект, который затем изменяли вызывающие стороны, но эти подходы были громоздкими.341342В Python 2.5 появился простой способ передачи значений в генератор. [`yield`](https://python-all.ru/3.0/reference/simple_stmts.html#yield) стало выражением, возвращающим значение, которое можно присвоить переменной или использовать в других операциях:343344```python345val = (yield i)346```347348Рекомендуется **всегда** заключать выражение `yield` в круглые скобки, когда вы что-то делаете с возвращаемым значением, как в примере выше. Скобки не всегда обязательны, но проще всегда их добавлять, чем запоминать, когда они нужны.349350(В PEP 342 объясняются точные правила: выражение с `yield` всегда должно быть заключено в скобки, за исключением случаев, когда оно находится на верхнем уровне в правой части присваивания. Это означает, что можно написать `val = yield i`, но необходимо использовать скобки при наличии операции, как в `val = (yield i) + 12`.)351352Значения отправляются в генератор вызовом его метода `send(value)`. Этот метод возобновляет код генератора, и выражение `yield` возвращает указанное значение. Если вызывается обычный метод `__next__()`, то `yield` возвращает `None`.353354Вот простой счётчик, который увеличивается на 1 и позволяет изменять значение внутреннего счётчика.355356```python357def counter (maximum):358    i = 0359    while i < maximum:360        val = (yield i)361        # Если передано значение, изменить counter.362        if val is not None:363            i = val364        else:365            i += 1366```367368А вот пример изменения счётчика:369370```python371>>> it = counter(10)372>>> next(it)3730374>>> next(it)3751376>>> it.send(8)3778378>>> next(it)3799380>>> next(it)381Traceback (most recent call last):382  File "t.py", line 15, in ?383    it.next()384StopIteration385```386387Поскольку `yield` часто будет возвращать `None`, следует всегда проверять этот случай. Не используйте его значение в выражениях, если вы не уверены, что метод `send()` будет единственным методом, используемым для возобновления генераторной функции.388389В дополнение к `send()` у генераторов есть два других новых метода:390391- `throw(type, value=None, traceback=None)` используется для возбуждения исключения внутри генератора; исключение возбуждается выражением `yield`, на котором выполнение генератора приостановлено.392- `close()` возбуждает исключение [`GeneratorExit`](https://python-all.ru/3.0/library/exceptions.html#exceptions.GeneratorExit) внутри генератора для завершения итерации. При получении этого исключения код генератора должен либо возбудить [`GeneratorExit`](https://python-all.ru/3.0/library/exceptions.html#exceptions.GeneratorExit), либо [`StopIteration`](https://python-all.ru/3.0/library/exceptions.html#exceptions.StopIteration); перехватывать исключение и делать что-либо ещё недопустимо и вызовет [`RuntimeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.RuntimeError). `close()` также будет вызван сборщиком мусора Python при сборке мусора для генератора.393394  Если нужно выполнить код очистки при возникновении [`GeneratorExit`](https://python-all.ru/3.0/library/exceptions.html#exceptions.GeneratorExit), лучше использовать блок `try: ... finally:` вместо перехвата [`GeneratorExit`](https://python-all.ru/3.0/library/exceptions.html#exceptions.GeneratorExit).395396Совокупный эффект этих изменений – превращение генераторов из однонаправленных производителей информации одновременно и в производителей, и в потребителей.397398Генераторы также становятся **корутинами** – более обобщённой формой подпрограмм. Подпрограммы вызываются в одной точке и завершаются в другой (начало функции и оператор `return`), но корутины можно вызывать, приостанавливать и возобновлять в разных точках (операторы `yield`).399400## Встроенные функции401402Рассмотрим подробнее встроенные функции, часто используемые с итераторами.403404Две встроенные функции Python, [`map()`](https://python-all.ru/3.0/library/functions.html#map) и [`filter()`](https://python-all.ru/3.0/library/functions.html#filter), дублируют возможности генераторных выражений:405406**`map(f, iterA, iterB, ...)` возвращает итератор по последовательности**407408`f(iterA[0], iterB[0]), f(iterA[1], iterB[1]), f(iterA[2], iterB[2]), ...`.409410> ```python411> >>> def upper(s):412> ...     return s.upper()413> ```414>415> ```python416> >>> list(map(upper, ['sentence', 'fragment']))417> ['SENTENCE', 'FRAGMENT']418> >>> [upper(s) for s in ['sentence', 'fragment']]419> ['SENTENCE', 'FRAGMENT']420> ```421422Конечно, того же эффекта можно достичь с помощью спискового включения.423424`filter(predicate, iter)` возвращает итератор по всем элементам последовательности, удовлетворяющим определённому условию, и аналогично дублируется списковыми включениями. **Предикат** – это функция, возвращающая истинностное значение некоторого условия; для использования с [`filter()`](https://python-all.ru/3.0/library/functions.html#filter) предикат должен принимать одно значение.425426> ```python427> >>> def is_even(x):428> ...     return (x % 2) == 0429> ```430>431> ```python432> >>> list(filter(is_even, range(10)))433> [0, 2, 4, 6, 8]434> ```435436Это также можно записать в виде спискового включения:437438```python439>>> list(x for x in range(10) if is_even(x))440[0, 2, 4, 6, 8]441```442443`enumerate(iter)` нумерует элементы итерируемого объекта, возвращая кортежи из двух элементов, содержащие номер и каждый элемент.444445```python446>>> for item in enumerate(['subject', 'verb', 'object']):447...     print(item)448(0, 'subject')449(1, 'verb')450(2, 'object')451```452453[`enumerate()`](https://python-all.ru/3.0/library/functions.html#enumerate) часто используется при обходе списка для записи индексов, на которых выполняются определённые условия:454455```python456f = open('data.txt', 'r')457for i, line in enumerate(f):458    if line.strip() == '':459        print('Blank line at line #%i' % i)460```461462`sorted(iterable, [key=None], [reverse=False])` собирает все элементы итерируемого объекта в список, сортирует его и возвращает отсортированный результат. Аргументы `key` и `reverse` передаются методу `.sort()` построенного списка.463464```python465>>> import random466>>> # Сгенерировать 8 случайных чисел в диапазоне [0, 10000).467>>> rand_list = random.sample(range(10000), 8)468>>> rand_list469[769, 7953, 9828, 6431, 8442, 9878, 6213, 2207]470>>> sorted(rand_list)471[769, 2207, 6213, 6431, 7953, 8442, 9828, 9878]472>>> sorted(rand_list, reverse=True)473[9878, 9828, 8442, 7953, 6431, 6213, 2207, 769]474```475476(Более подробное обсуждение сортировки см. в мини-руководстве по сортировке в вики Python: [http://wiki.python.org/moin/HowTo/Sorting](https://python-all.ru/3.0/howto/functional.html).)477478Встроенные функции `any(iter)` и `all(iter)` проверяют истинностные значения содержимого итерируемого объекта. [`any()`](https://python-all.ru/3.0/library/functions.html#any) возвращает True, если хотя бы один элемент итерируемого объекта истинен, а [`all()`](https://python-all.ru/3.0/library/functions.html#all) возвращает True, если все элементы истинны:479480```python481>>> any([0,1,0])482True483>>> any([0,0,0])484False485>>> any([1,1,1])486True487>>> all([0,1,0])488False489>>> all([0,0,0])490False491>>> all([1,1,1])492True493```494495`zip(iterA, iterB, ...)` берёт по одному элементу из каждого итерируемого объекта и возвращает их в кортеже:496497```python498zip(['a', 'b', 'c'], (1, 2, 3)) =>499  ('a', 1), ('b', 2), ('c', 3)500```501502Он не создаёт список в памяти и не исчерпывает все входные итераторы до возврата; вместо этого кортежи создаются и возвращаются только по запросу. (Технический термин для такого поведения – [ленивые вычисления](https://python-all.ru/3.0/howto/functional.html).)503504Этот итератор предназначен для использования с итерируемыми объектами одинаковой длины. Если длины различаются, результирующий поток данных будет иметь длину кратчайшего итерируемого объекта.505506```python507zip(['a', 'b'], (1, 2, 3)) =>508  ('a', 1), ('b', 2)509```510511Тем не менее, так делать не стоит, поскольку из более длинных итераторов может быть взят и отброшен элемент. Это означает, что использовать эти итераторы дальше нельзя – есть риск пропустить отброшенный элемент.512513## Модуль itertools514515Модуль [`itertools`](https://python-all.ru/3.0/library/itertools.html#module-itertools) содержит ряд часто используемых итераторов, а также функции для комбинирования нескольких итераторов. В этом разделе будут представлены возможности модуля на небольших примерах.516517Функции модуля делятся на несколько широких категорий:518519- Функции, создающие новый итератор на основе существующего.520- Функции для передачи элементов итератора в качестве аргументов функции.521- Функции для выбора части вывода итератора.522- Функция для группировки вывода итератора.523524### Создание новых итераторов525526`itertools.count(n)` возвращает бесконечный поток целых чисел, увеличивающихся на 1 каждый раз. При необходимости можно указать начальное число; по умолчанию оно равно 0:527528```python529itertools.count() =>530  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...531itertools.count(10) =>532  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...533```534535`itertools.cycle(iter)` сохраняет копию содержимого переданного итерируемого объекта и возвращает новый итератор, возвращающий его элементы от первого до последнего. Этот новый итератор будет бесконечно повторять эти элементы.536537```python538itertools.cycle([1,2,3,4,5]) =>539  1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...540```541542`itertools.repeat(elem, [n])` возвращает переданный элемент `n` раз или бесконечно, если `n` не указан.543544```python545itertools.repeat('abc') =>546  abc, abc, abc, abc, abc, abc, abc, abc, abc, abc, ...547itertools.repeat('abc', 5) =>548  abc, abc, abc, abc, abc549```550551`itertools.chain(iterA, iterB, ...)` принимает произвольное количество итерируемых объектов и возвращает все элементы первого итератора, затем все элементы второго и так далее, пока все итерируемые объекты не будут исчерпаны.552553```python554itertools.chain(['a', 'b', 'c'], (1, 2, 3)) =>555  a, b, c, 1, 2, 3556```557558`itertools.islice(iter, [start], stop, [step])` возвращает поток, представляющий собой срез итератора. С одним аргументом `stop` он вернёт первые `stop` элементов. Если указать начальный индекс, получится `stop-start` элементов, а если указать значение для `step`, элементы будут соответствующим образом пропускаться. В отличие от срезов строк и списков Python, для `start`, `stop` и `step` нельзя использовать отрицательные значения.559560```python561itertools.islice(range(10), 8) =>562  0, 1, 2, 3, 4, 5, 6, 7563itertools.islice(range(10), 2, 8) =>564  2, 3, 4, 5, 6, 7565itertools.islice(range(10), 2, 8, 2) =>566  2, 4, 6567```568569`itertools.tee(iter, [n])` дублирует итератор; он возвращает `n` независимых итераторов, каждый из которых будет возвращать содержимое исходного итератора. Если не указать значение для `n`, по умолчанию используется 2. Дублирование итераторов требует сохранения части содержимого исходного итератора, поэтому это может потреблять значительный объём памяти, если итератор большой и один из новых итераторов используется чаще остальных.570571```python572itertools.tee( itertools.count() ) =>573   iterA, iterB574575where iterA ->576   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...577578and   iterB ->579   0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ...580```581582### Применение функций к элементам583584Модуль `operator` содержит набор функций, соответствующих операторам Python. Некоторые примеры: `operator.add(a, b)` (складывает два значения), `operator.ne(a, b)` (то же, что `a!=b`), и `operator.attrgetter('id')` (возвращает вызываемый объект, который получает атрибут `"id"`).585586`itertools.starmap(func, iter)` предполагает, что итерируемый объект будет возвращать поток кортежей, и вызывает `f()`, используя эти кортежи в качестве аргументов:587588```python589itertools.starmap(os.path.join,590                  [('/usr', 'bin', 'java'), ('/bin', 'python'),591                   ('/usr', 'bin', 'perl'),('/usr', 'bin', 'ruby')])592=>593  /usr/bin/java, /bin/python, /usr/bin/perl, /usr/bin/ruby594```595596### Выбор элементов597598Ещё одна группа функций выбирает подмножество элементов итератора на основе предиката.599600`itertools.filterfalse(predicate, iter)` – наоборот, возвращает все элементы, для которых предикат возвращает false:601602```python603itertools.filterfalse(is_even, itertools.count()) =>604  1, 3, 5, 7, 9, 11, 13, 15, ...605```606607`itertools.takewhile(predicate, iter)` возвращает элементы до тех пор, пока предикат возвращает true. Как только предикат возвращает false, итератор сигнализирует об окончании результатов.608609```python610def less_than_10(x):611    return (x < 10)612613itertools.takewhile(less_than_10, itertools.count()) =>614  0, 1, 2, 3, 4, 5, 6, 7, 8, 9615616itertools.takewhile(is_even, itertools.count()) =>617  0618```619620`itertools.dropwhile(predicate, iter)` отбрасывает элементы, пока предикат возвращает true, а затем возвращает остальные результаты итерируемого объекта.621622```python623itertools.dropwhile(less_than_10, itertools.count()) =>624  10, 11, 12, 13, 14, 15, 16, 17, 18, 19, ...625626itertools.dropwhile(is_even, itertools.count()) =>627  1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...628```629630### Группировка элементов631632Последняя функция, которую я рассмотрю, `itertools.groupby(iter, key_func=None)` – самая сложная. `key_func(elem)` – это функция, которая вычисляет ключевое значение для каждого элемента, возвращаемого итерируемым объектом. Если не указать функцию ключа, ключом будет сам элемент.633634`groupby()` собирает все последовательные элементы из исходного итерируемого объекта, имеющие одинаковое ключевое значение, и возвращает поток из 2-кортежей, содержащих ключевое значение и итератор для элементов с этим ключом.635636```python637city_list = [('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL'),638             ('Anchorage', 'AK'), ('Nome', 'AK'),639             ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ'),640             ...641            ]642643def get_state (city_state):644    return city_state[1]645646itertools.groupby(city_list, get_state) =>647  ('AL', iterator-1),648  ('AK', iterator-2),649  ('AZ', iterator-3), ...650651where652iterator-1 =>653  ('Decatur', 'AL'), ('Huntsville', 'AL'), ('Selma', 'AL')654iterator-2 =>655  ('Anchorage', 'AK'), ('Nome', 'AK')656iterator-3 =>657  ('Flagstaff', 'AZ'), ('Phoenix', 'AZ'), ('Tucson', 'AZ')658```659660`groupby()` предполагает, что содержимое исходного итерируемого объекта уже отсортировано по ключу. Обратите внимание, что возвращаемые итераторы также используют исходный итерируемый объект, поэтому необходимо обработать результаты итератора-1, прежде чем запрашивать итератор-2 и соответствующий ему ключ.661662## Модуль functools663664Модуль [`functools`](https://python-all.ru/3.0/library/functools.html#module-functools) в Python 2.5 содержит несколько функций высшего порядка. **Функция высшего порядка** принимает на вход одну или несколько функций и возвращает новую функцию. Самый полезный инструмент в этом модуле – функция [`functools.partial()`](https://python-all.ru/3.0/library/functools.html#functools.partial).665666В программах, написанных в функциональном стиле, иногда требуется создать варианты существующих функций с уже заполненными некоторыми параметрами. Рассмотрим функцию Python `f(a, b, c)`; возможно, понадобится создать новую функцию `g(b, c)`, эквивалентную `f(1, b, c)`; то есть подставляется значение для одного из параметров `f()`. Это называется «частичное применение функции».667668Конструктор `partial` принимает аргументы `(function, arg1, arg2, ... kwarg1=value1, kwarg2=value2)`. Полученный объект является вызываемым, поэтому его можно просто вызвать, чтобы выполнить `function` с переданными аргументами.669670Вот небольшой, но реалистичный пример:671672```python673import functools674675def log (message, subsystem):676    "Write the contents of 'message' to the specified subsystem."677    print('%s: %s' % (subsystem, message))678    ...679680server_log = functools.partial(log, subsystem='server')681server_log('Unable to open socket')682```683684`functools.reduce(func, iter, [initial_value])` последовательно применяет операцию ко всем элементам итерируемого объекта, поэтому не может применяться к бесконечным итерируемым объектам. (Обратите внимание, что она находится не в модуле [`builtins`](https://python-all.ru/3.0/library/builtins.html#module-builtins), а в модуле [`functools`](https://python-all.ru/3.0/library/functools.html#module-functools).) `func` должна быть функцией, принимающей два элемента и возвращающей одно значение. [`functools.reduce()`](https://python-all.ru/3.0/library/functools.html#functools.reduce) берёт первые два элемента A и B, возвращённые итератором, и вычисляет `func(A, B)`. Затем он запрашивает третий элемент C, вычисляет `func(func(A, B), C)`, объединяет этот результат с четвёртым элементом и продолжает до тех пор, пока итерируемый объект не будет исчерпан. Если итерируемый объект вообще не возвращает значений, возбуждается исключение [`TypeError`](https://python-all.ru/3.0/library/exceptions.html#exceptions.TypeError). Если указано начальное значение, оно используется в качестве отправной точки, и `func(initial_value, A)` – первое вычисление.685686```python687>>> import operator, functools688>>> functools.reduce(operator.concat, ['A', 'BB', 'C'])689'ABBC'690>>> functools.reduce(operator.concat, [])691Traceback (most recent call last):692  ...693TypeError: reduce() of empty sequence with no initial value694>>> functools.reduce(operator.mul, [1,2,3], 1)6956696>>> functools.reduce(operator.mul, [], 1)6971698```699700Если использовать [`operator.add()`](https://python-all.ru/3.0/library/operator.html#operator.add) с [`functools.reduce()`](https://python-all.ru/3.0/library/functools.html#functools.reduce), то будут просуммированы все элементы итерируемого объекта. Этот случай настолько распространён, что для его вычисления существует специальная встроенная функция [`sum()`](https://python-all.ru/3.0/library/functions.html#sum):701702```python703>>> import functools704>>> functools.reduce(operator.add, [1,2,3,4], 0)70510706>>> sum([1,2,3,4])70710708>>> sum([])7090710```711712Однако во многих случаях использования [`functools.reduce()`](https://python-all.ru/3.0/library/functools.html#functools.reduce) бывает понятнее просто написать очевидный цикл [`for`](https://python-all.ru/3.0/reference/compound_stmts.html#for):713714```python715import functools716# Вместо:717product = functools.reduce(operator.mul, [1,2,3], 1)718719# Можно написать:720product = 1721for i in [1,2,3]:722    product *= i723```724725### Модуль operator726727Модуль [`operator`](https://python-all.ru/3.0/library/operator.html#module-operator) упоминался ранее. Он содержит набор функций, соответствующих операторам Python. Эти функции часто полезны в коде в функциональном стиле, поскольку избавляют от необходимости писать тривиальные функции, выполняющие одну операцию.728729Некоторые функции этого модуля:730731- Математические операции: `add()`, `sub()`, `mul()`, `floordiv()`, `abs()`, ...732- Логические операции: `not_()`, `truth()`.733- Битовые операции: `and_()`, `or_()`, `invert()`.734- Сравнения: `eq()`, `ne()`, `lt()`, `le()`, `gt()` и `ge()`.735- Идентичность объектов: `is_()`, `is_not()`.736737Полный список – в документации модуля operator.738739### Модуль functional740741Модуль functional[functional module](https://python-all.ru/3.0/howto/functional.html) от Коллина Уинтера предоставляет ряд более продвинутых инструментов для функционального программирования. Он также переопределяет несколько встроенных функций Python, пытаясь сделать их более интуитивно понятными для тех, кто привык к функциональному программированию на других языках.742743Этот раздел содержит введение в некоторые из наиболее важных функций в `functional`; полная документация доступна на [веб-сайте проекта](https://python-all.ru/3.0/howto/functional.html).744745`compose(outer, inner, unpack=False)`746747Функция `compose()` реализует композицию функций. Другими словами, она возвращает обёртку вокруг вызываемых объектов `outer` и `inner`, такую, что возвращаемое значение от `inner` передаётся напрямую в `outer`. То есть,748749```python750>>> def add(a, b):751...     return a + b752...753>>> def double(a):754...     return 2 * a755...756>>> compose(double, add)(5, 6)75722758```759760эквивалентна761762```python763>>> double(add(5, 6))76422765```766767Ключевое слово `unpack` предоставлено для обхода того факта, что функции Python не всегда [полностью каррированы](https://python-all.ru/3.0/howto/functional.html). По умолчанию ожидается, что функция `inner` будет возвращать один объект, а функция `outer` будет принимать один аргумент. Установка аргумента `unpack` заставляет `compose` ожидать кортеж от `inner`, который будет развёрнут перед передачей в `outer`. Проще говоря,768769```python770compose(f, g)(5, 6)771```772773эквивалентно:774775```python776f(g(5, 6))777```778779while780781```python782compose(f, g, unpack=True)(5, 6)783```784785эквивалентно:786787```python788f(*g(5, 6))789```790791Хотя `compose()` принимает только две функции, тривиально построить версию, которая будет компоновать любое количество функций. Мы будем использовать [`functools.reduce()`](https://python-all.ru/3.0/library/functools.html#functools.reduce), `compose()` и `partial()` (последняя из которых предоставляется как `functional`, так и `functools`).792793```python794from functional import compose, partial795import functools796797multi_compose = partial(functools.reduce, compose)798```799800Мы также можем использовать `map()`, `compose()` и `partial()` для создания версии `"".join(...)`, преобразующей свои аргументы в строку:801802```python803from functional import compose, partial804805join = compose("".join, partial(map, str))806```807808`flip(func)`809810`flip()` оборачивает вызываемый объект в `func` и заставляет его получать свои позиционные аргументы в обратном порядке.811812```python813>>> def triple(a, b, c):814...     return (a, b, c)815...816>>> triple(5, 6, 7)817(5, 6, 7)818>>>819>>> flipped_triple = flip(triple)820>>> flipped_triple(5, 6, 7)821(7, 6, 5)822```823824`foldl(func, start, iterable)`825826`foldl()` принимает бинарную функцию, начальное значение (обычно своего рода «ноль») и итерируемый объект. Функция применяется к начальному значению и первому элементу списка, затем к результату и второму элементу списка, затем к результату и третьему элементу списка и так далее.827828Это означает, что вызов, например:829830```python831foldl(f, 0, [1, 2, 3])832```833834эквивалентно:835836```python837f(f(f(0, 1), 2), 3)838```839840`foldl()` примерно эквивалентна следующей рекурсивной функции:841842```python843def foldl(func, start, seq):844    if len(seq) == 0:845        return start846847    return foldl(func, func(start, seq[0]), seq[1:])848```849850Говоря об эквивалентности, приведённый выше вызов `foldl` можно выразить с помощью встроенной функции [`functools.reduce()`](https://python-all.ru/3.0/library/functools.html#functools.reduce) следующим образом:851852```python853import functools854functools.reduce(f, [1, 2, 3], 0)855```856857Мы можем использовать `foldl()`, `operator.concat()` и `partial()` для написания более чистой и эстетичной версии идиомы Python `"".join(...)`:858859```python860from functional import foldl, partial from operator import concat861862join = partial(foldl, concat, "")863```864865## Небольшие функции и лямбда-выражение866867При написании программ в функциональном стиле часто требуются небольшие функции, которые выступают в роли предикатов или каким-либо образом комбинируют элементы.868869Если есть подходящая встроенная функция Python или функция из модуля, не нужно определять новую функцию:870871```python872stripped_lines = [line.strip() for line in lines]873existing_files = filter(os.path.exists, file_list)874```875876Если нужной функции не существует, её нужно написать. Один из способов написания небольших функций – использовать оператор `lambda`. `lambda` принимает ряд параметров и выражение, объединяющее эти параметры, и создаёт небольшую функцию, возвращающую значение выражения:877878```python879lowercase = lambda x: x.lower()880881print_assign = lambda name, value: name + '=' + str(value)882883adder = lambda x, y: x+y884```885886Альтернатива – просто использовать инструкцию `def` и определить функцию обычным способом:887888```python889def lowercase(x):890    return x.lower()891892def print_assign(name, value):893    return name + '=' + str(value)894895def adder(x,y):896    return x + y897```898899Какой вариант предпочтительнее? Это вопрос стиля; я обычно стараюсь избегать использования `lambda`.900901Одна из причин моего предпочтения в том, что `lambda` весьма ограничена в определении функций. Результат должен быть вычислим как одно выражение, а это значит, что нельзя использовать многосторонние сравнения `if... elif... else` или инструкции `try... except`. Если попытаться сделать слишком много в одной `lambda`, получится чрезмерно сложное выражение, которое трудно читать. Быстро, что делает следующий код?902903```python904import functools905total = functools.reduce(lambda a, b: (0, a[1] + b[1]), items)[1]906```907908Можно разобраться, но требуется время, чтобы распутать выражение и понять, что происходит. Использование коротких вложенных инструкций `def` немного улучшает ситуацию:909910```python911import functools912def combine (a, b):913    return 0, a[1] + b[1]914915total = functools.reduce(combine, items)[1]916```917918Но лучше всего было бы просто использовать цикл `for`:919920```python921total = 0922for a, b in items:923    total += b924```925926Или встроенную функцию [`sum()`](https://python-all.ru/3.0/library/functions.html#sum) и генераторное выражение:927928```python929total = sum(b for a,b in items)930```931932Многие случаи использования [`functools.reduce()`](https://python-all.ru/3.0/library/functools.html#functools.reduce) становятся понятнее, если записать их в виде циклов `for`.933934Фредрик Лунд однажды предложил следующий набор правил для рефакторинга случаев использования `lambda`:9359361. Написать лямбда-функцию.9372. Написать комментарий, объясняющий, что, черт возьми, делает эта лямбда.9383. Некоторое время изучать комментарий и придумать имя, отражающее суть комментария.9394. Преобразовать лямбду в инструкцию def, используя это имя.9405. Удалить комментарий.941942Мне очень нравятся эти правила, но вы вольны не соглашаться с тем, что этот стиль без лямбда лучше.943944## История изменений и благодарности945946Автор хотел бы поблагодарить следующих людей за предложения, исправления и помощь с различными черновиками этой статьи: Ian Bicking, Nick Coghlan, Nick Efford, Raymond Hettinger, Jim Jewett, Mike Krell, Leandro Lameiro, Jussi Salmela, Collin Winter, Blake Winton.947948Версия 0.1: опубликована 30 июня 2006 г.949950Версия 0.11: опубликована 1 июля 2006 г. Исправлены опечатки.951952Версия 0.2: опубликована 10 июля 2006 г. Разделы о выражениях-генераторах и списковых включениях объединены в один. Исправлены опечатки.953954Версия 0.21: добавлены дополнительные ссылки, предложенные в списке рассылки tutor.955956Версия 0.30: добавлен раздел о модуле `functional`, написанный Коллином Уинтером; добавлен краткий раздел о модуле operator; несколько других правок.957958## Ссылки959960### Общие961962**Структура и интерпретация компьютерных программ**, авторы Гарольд Абельсон и Джеральд Джей Сассман при участии Джули Сассман. Полный текст по адресу [http://mitpress.mit.edu/sicp/](https://python-all.ru/3.0/howto/functional.html). В этом классическом учебнике по информатике главы 2 и 3 рассматривают использование последовательностей и потоков для организации потока данных внутри программы. В книге для примеров используется Scheme, но многие подходы к проектированию, описанные в этих главах, применимы к коду на Python в функциональном стиле.963964[http://www.defmacro.org/ramblings/fp.html](https://python-all.ru/3.0/howto/functional.html): Общее введение в функциональное программирование на примерах Java, содержащее обширное историческое введение.965966[http://en.wikipedia.org/wiki/Functional\_programming](https://python-all.ru/3.0/howto/functional.html): общая статья в Википедии, описывающая функциональное программирование.967968[http://en.wikipedia.org/wiki/Coroutine](https://python-all.ru/3.0/howto/functional.html): статья о корутинах.969970[http://en.wikipedia.org/wiki/Currying](https://python-all.ru/3.0/howto/functional.html): статья о концепции каррирования.971972### Специфическое для Python973974[http://gnosis.cx/TPiP/](https://python-all.ru/3.0/howto/functional.html): Первая глава книги Дэвида Мертца *Text Processing in Python* рассматривает функциональное программирование для обработки текста в разделе «Использование функций высшего порядка в обработке текста».975976Мерц также написал трёхчастную серию статей по функциональному программированию для сайта IBM DeveloperWorks; см. [часть 1](https://python-all.ru/3.0/howto/functional.html), [часть 2](https://python-all.ru/3.0/howto/functional.html) и [часть 3](https://python-all.ru/3.0/howto/functional.html).977978### Документация Python979980Документация по модулю [`itertools`](https://python-all.ru/3.0/library/itertools.html#module-itertools).981982Документация для модуля [`operator`](https://python-all.ru/3.0/library/operator.html#module-operator).983984[**PEP 289**](https://python-all.ru/3.0/howto/functional.html): “Генераторные выражения”985986[**PEP 342**](https://python-all.ru/3.0/howto/functional.html): “Корутины через улучшенные генераторы” описывает новые возможности генераторов в Python 2.5.987