{ "cells": [ { "cell_type": "markdown", "id": "46044c32", "metadata": {}, "source": [ "# Функциональное программирование #" ] }, { "cell_type": "markdown", "id": "b1d0b1e3", "metadata": {}, "source": [ "Мы с вами разобрали то, как работают и определяются функции в Python'e и настало время разобраться с таким сложным концептом как функциональное программирование.\n", "\n", "Что же это такое? Чтобы понять, что такое функциональное программирование, нужно понимать несколько особенностей.\n", "\n", "Во-первых, функции в Python'e - это такие же объекты, как и, например, строки, списки или классы.\n", "Их можно передавать в функции другие, их можно возвращать из функций, их можно создавать на лету, то есть это объекты первого класса.\n", "Например, мы можем определить функцию `caller`, которая принимает другую функцию." ] }, { "cell_type": "code", "execution_count": 1, "id": "dd050baa", "metadata": {}, "outputs": [], "source": [ "def caller(func, params):\n", " return func(*params)" ] }, { "cell_type": "markdown", "id": "60905ede", "metadata": {}, "source": [ "Функцию можно передавать в функцию, как я вам говорил. Она принимает другую функцию и какой-то список параметров, и, собственно, эта функция вызывается этим списком параметров с использованием звездочки, с которой мы только что с вами разобрались.\n", "\n", "Определим другую функцию — обычную `printer`, которая просто выводит какую-то строчку. Мы можем в наш `caller` передать функцию `printer` и вызвать её. Это может быть функция `printer` в нашем `caller`'е, а может быть какая-нибудь другая функция - нам неважно." ] }, { "cell_type": "code", "execution_count": 3, "id": "bcfb80f1", "metadata": {}, "outputs": [], "source": [ "def printer(name, origin):\n", " print(f\"I'm {name} of {origin}!\")" ] }, { "cell_type": "code", "execution_count": 4, "id": "5c79161f", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "I'm Moana of Motunui!\n" ] } ], "source": [ "caller(printer, [\"Moana\", \"Motunui\"])" ] }, { "cell_type": "markdown", "id": "29db28ad", "metadata": {}, "source": [ "Главное, чтобы мы передали туда функцию и список параметров. Итак, мы вызываем наш `caller` с функцией `printer` и передаем туда список параметров и у нас выводится строка.\n", "\n", "Собственно, главное, что нужно вынести из этого примера, что функции можно передавать другие функции. Это такие же объекты." ] }, { "cell_type": "markdown", "id": "4aae0263", "metadata": {}, "source": [ "Ещё один важный момент. Функции можно не только передавать, их еще можно создавать внутри других функций. Например, мы можем определить функцию `get_multiplier` и внутри функции `get_multiplier` определить другую функцию, и эта функция будет `inner`. Она будет просто умножать два аргумента ей переданных." ] }, { "cell_type": "code", "execution_count": 6, "id": "77503f82", "metadata": {}, "outputs": [], "source": [ "def get_multiplier():\n", " def inner(a, b):\n", " return a * b\n", " \n", " return inner" ] }, { "cell_type": "markdown", "id": "95090fff", "metadata": {}, "source": [ "Мы можем вызвать функцию `get_multiplier` и получить обычный `multiplier`, который умножает, например, 10 на 11, то есть мы определили функцию, которая возвращает другую функцию." ] }, { "cell_type": "code", "execution_count": 7, "id": "644b0061", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "110" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "multiplier = get_multiplier()\n", "multiplier(10, 11)" ] }, { "cell_type": "markdown", "id": "2a1490a6", "metadata": {}, "source": [ "Пока ничего сложного. Обратите внимание, что именно `multiplier`'ы — это `inner`, потому что мы вернули новую функцию и `multiplier` перестал быть тем, каким мы его знали раньше. Он стал `inner`'ом. " ] }, { "cell_type": "code", "execution_count": 8, "id": "2978a144", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "inner\n" ] } ], "source": [ "print(multiplier.__name__)" ] }, { "cell_type": "markdown", "id": "18d1ca79", "metadata": {}, "source": [ "Гораздо более интересный момент - это если мы в наш `get_multiplier` что-нибудь передадим. Давайте попробуем определить функцию `inner`, которая будет принимать один аргумент и умножать она его будет всегда на то самое число, которое мы передали в `get_multiplier`." ] }, { "cell_type": "code", "execution_count": 13, "id": "3c7d8cef", "metadata": {}, "outputs": [], "source": [ "def get_multiplier(number):\n", " def inner(a):\n", " return a * number\n", " \n", " return inner" ] }, { "cell_type": "markdown", "id": "f00d88e1", "metadata": {}, "source": [ "Смотрите, мы передаем `get_multiplier` двойку и получаем функцию, которая всегда умножает переданный ей аргумент на двойку." ] }, { "cell_type": "code", "execution_count": 14, "id": "06513593", "metadata": {}, "outputs": [], "source": [ "multiplier_by_2 = get_multiplier(2)" ] }, { "cell_type": "markdown", "id": "840f794a", "metadata": {}, "source": [ "Например, мы передали в `multiplier_by_2` десятку и получили 20." ] }, { "cell_type": "code", "execution_count": 10, "id": "256d8b08", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "20" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "multiplier_by_2(10)" ] }, { "cell_type": "markdown", "id": "e72a8eb0", "metadata": {}, "source": [ "Эта концепция называется \"замыканием\". В данном случае у нас используется `number` из скопа выше, из области видимости функции `get_multiplier` и это очень важный момент, который в дальнейшем будет использоваться, например, в декораторах.\n", "\n", "Часто бывает необходимо определить какие-то свои функции, которые принимают другие функции, но ещё чаще эти функции используют примерно одинаково, например, для того, чтобы применить какую-то функцию к набору элементов.\n", " \n", "Существует несколько стандартных функций, которые вам позволяют в функциональном стиле работать.\n", "\n", "Одна из таких функций - это `map`. Функция `map` принимает функцию и какой-то итерабельный объект, то есть, например, список или что-то, по чему можно итерироваться, и применяет полученную функцию к этому итерабельному объекту.\n", "\n", "Например, мы можем определить функцию `squarify`, которая просто принимает число и возвращает вторую степень его, то есть возвращает квадрат." ] }, { "cell_type": "code", "execution_count": 15, "id": "7460b616", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16]" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def squarify(a):\n", " return a ** 2\n", "\n", "list(map(squarify, range(5)))" ] }, { "cell_type": "markdown", "id": "3fded733", "metadata": {}, "source": [ "Функция `map` работает очень просто. Мы передаем ей функцию `squarify` и `range`, то есть итератор от ноля до четырех. Функция `map` бежит по этому итерабельному объекту, то есть по `rang`'у, и применяет `squarify` к элементу одному за другим. В данном случае у нас получается список от 0 до 16 квадратов. Обратите внимание, я вызываю функцию `list` вокруг `map`'а, потому что `map` по умолчанию возвращает `map object`. Это не список, а какой-то объект внутренний, которому самое главное, что нужно — итерироваться.\n", "\n", "Очень часто нам просто необходимо итерироваться по этому объекту, нам не важно конкретно, чтобы это был список. Собственно, что здесь происходит, можно посмотреть на чистом Python'e. У нас как бы в начале создается `squared_list`, и потом просто мы в цикле в этот `squared_list` записываем применение функции к текущему `number`'у.\n", "\n", "Однако, это делается всё довольно просто и коротко с помощью функции `map`. " ] }, { "cell_type": "code", "execution_count": 16, "id": "726ace85", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 4, 9, 16]\n" ] } ], "source": [ "squared_list = []\n", "\n", "for number in range(5):\n", " squared_list.append(squarify(number))\n", " \n", "print(squared_list)" ] }, { "cell_type": "markdown", "id": "6518fd00", "metadata": {}, "source": [ "Ещё одна функция, которая часто используется в контексте функционального программирования, это функция `filter`. Функция `filter` вам позволяет фильтровать по какому-то предикату итерабельный объект.\n", "\n", "У вас есть какое-то условие, в данном случае — это функция, которая принимает число и проверяет, что это число больше ноля и возвращает `True` в этом случае, если это ноль или меньше, то возвращается `False`." ] }, { "cell_type": "code", "execution_count": 17, "id": "ee4c3b6f", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def is_positive(a):\n", " return a > 0\n", "\n", "list(filter(is_positive, range(-2, 3)))" ] }, { "cell_type": "markdown", "id": "4442abda", "metadata": {}, "source": [ "У нас `filter` принимает какую-то функцию и фильтрует все аргументы внутри итерабельного объекта по этому предикату. Например, мы можем оставить в нашем списке, или в нашем `rang`'е, в нашем итерабельном объекте только положительные числа. \n", "\n", "Собственно, на чистом Python'e это выглядело бы как-то так. У нас есть наш `positive_list` и мы в цикле просто проверяем, что если у нас функция возвращает `True`, то мы оставляем это значение, то есть прибавляем в `positive_list` это число. Иначе мы просто продолжаем итерацию и ничего не делаем. Можно убедиться, что выводится именно то же самое." ] }, { "cell_type": "code", "execution_count": 18, "id": "ae7405d5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2]\n" ] } ], "source": [ "positive_list = []\n", "\n", "for number in range(-2, 3):\n", " if is_positive(number):\n", " positive_list.append(number)\n", " \n", "print(positive_list)" ] }, { "cell_type": "markdown", "id": "d5f9f452", "metadata": {}, "source": [ "`Filter` и `map` позволяет вам лаконично и просто записать стандартные какие-то паттерны программирования." ] }, { "cell_type": "markdown", "id": "02a5ad50", "metadata": {}, "source": [ "Сейчас мы с вами разберем анонимные функции. Хотелось бы заметить, что несмотря на то, что `map` и `filter` очень мощны, не стоит злоупотреблять ими, потому что, если вы пишете довольно сложный код, который сложно разобрать, если вы вкладываете `map` в `map`, и `filter` в `filter`, пишете многострочные какие-то функциональные штуки, программистам сложно будет читать этот код. \n", "\n", "Чем сильнее, чем выше когнитивная нагрузка у программиста, тем хуже. Пожалуйста, не злоупотребляйте ими, хотя это довольно мощные конструкции.\n", "\n", "Итак, часто нам бывает необходимо определить какую-то функцию, например, `squarify` как мы определяли, но нам не нужно будет её применять в дальнейшем. Нам она интересна только в каком-то контексте. Например, возвести во вторую степень все элементы в этом списке, но мы не хотим использовать эту функцию потом. Нам на помощь в данном случае придут анонимные функции, так они называются в Python'e или `lambda` функции. `Lambda` позволяет вам определить функцию прям `in place`, то есть без оператора `def`, без литерала `def`. Используйте её прям на месте и забыли про нее потом. В данном случае мы делаем точно то же самое, что и в предыдущем примере, только определяем функцию не с помощью `def`'а, где-то вверху, а прямо на месте и пишем, что наша анонимная функция принимает один параметр и возводит его в квадрат. Всё." ] }, { "cell_type": "code", "execution_count": 19, "id": "312fd40e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda x: x ** 2, range(5)))" ] }, { "cell_type": "code", "execution_count": 20, "id": "01b7bc1c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "function" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(lambda x: x ** 2)" ] }, { "cell_type": "code", "execution_count": 21, "id": "5d3224c3", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 2]" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda x: x > 0, range(-2, 3)))" ] }, { "cell_type": "markdown", "id": "08af4100", "metadata": {}, "source": [ "Происходит то же самое, только у нас функция создается `in place`. Собственно, как видите, `map` точно так же принимает функцию и итерабельный объект. Ничего нового. `Lambda` — это действительно функция, но она анонимная. У нее нет имени, как видите, просто какая-то функция, принимающая один объект и возвращающая квадрат. То же самое можно сделать с `filter`'ом, и `filter` у нас примет функцию `lambda`, и `lambda` проверит, что число больше нуля." ] }, { "cell_type": "markdown", "id": "0f87bbbf", "metadata": {}, "source": [ "Давайте в качестве примера напишем функцию, которая превращает список чисел в список строк. Сделайте это самостоятельно." ] }, { "cell_type": "code", "execution_count": 22, "id": "0977cbb9", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def stringify_list(num_list):\n", " return list(map(str, num_list))\n", "\n", "stringify_list(range(10))" ] }, { "cell_type": "markdown", "id": "1a27fac1", "metadata": {}, "source": [ "Отлично, скорее всего у вас всё получилось. Ничего сложного не должно быть. Чтобы превратить список чисел в список строк нам нужно использовать функцию `map`, как вы могли догадаться, и передать функцию `map`, класс `str`. При применении класса `str` к числу, у нас получается строка и именно это нам и нужно. Нам нужно пробежаться по списку и всё передать в строку, то есть передать в конструктор `str`. Мы можем вызвать нашу функцию в `range` и получить список строк от нуля до девяти." ] }, { "cell_type": "markdown", "id": "a31320c1", "metadata": {}, "source": [ "Существует отличный модуль `functools`, который вам позволяет использовать функциональные особенности Python'а ещё лучше.\n", "\n", "Например, `functools` в последних версиях языка принесли функцию `reduce`, которая изначально была в общем скопе глобальных функций. Что делает `reduce`? `Reduce`, как вы уже могли догадаться, принимает функцию и итерабельный объект. Что она делает с ним? В данном случае у нас функция `multiply`, которая принимает два значения и перемножает их. Функция `reduce`, начиная слева направо, бежит и по цепочке применяет эту функцию." ] }, { "cell_type": "code", "execution_count": 23, "id": "6007f8fc", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "120" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from functools import reduce\n", "\n", "def multiply(a, b):\n", " return a * b\n", "\n", "reduce(multiply, [1, 2, 3, 4, 5])" ] }, { "cell_type": "markdown", "id": "28512684", "metadata": {}, "source": [ "Каким образом? В начале в функцию `multiply` попадают один и два, они перемножаются и запоминается двойка, у нас в памяти лежит двойка. Дальше, у нас эта двойка перемножается с тройкой. Таким образом у нас была двойка в памяти, берется следующий элемент, перемножается с тройкой. У нас уже шесть в памяти. Шесть в памяти умножается на четыре. У нас получается 24 и запоминается. 24 потом уже умножается на пять, получается 120, что мы получаем на входе. `Reduce` позволяет вам сжимать какие-то данные, применяя последовательно функцию и запоминая результат. То же самое можно сделать с помощью анонимных функций." ] }, { "cell_type": "code", "execution_count": 24, "id": "634a1fde", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "24" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(lambda x, y: x * y, range(1, 5))" ] }, { "cell_type": "markdown", "id": "6fad411a", "metadata": {}, "source": [ "Да, но получилось не 120 — `range`, как вы могли догадаться, пятерку не включает." ] }, { "cell_type": "code", "execution_count": 25, "id": "377ef58d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "120" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reduce(lambda x, y: x * y, range(1, 6))" ] }, { "cell_type": "markdown", "id": "0b35a963", "metadata": {}, "source": [ "Также в `functools` есть отличный метод `partial`, который вам позволяет модифицировать немного поведение функций. Например, у нас есть функция `greeter`, которая просто приветствует человека с помощью какого-то приветствия `greeting`. Например, может поздороваться с Майклом. И мы можем определить какой-то новый набор функций, подменив по умолчанию определенные параметры. " ] }, { "cell_type": "code", "execution_count": 26, "id": "9dfec3a9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, brother!\n", "Hello, sir!\n" ] } ], "source": [ "from functools import partial\n", "\n", "def greeter(person, greeting):\n", " return f\"{greeting}, {person}!\"\n", "\n", "hier = partial(greeter, greeting=\"Hi\")\n", "helloer = partial(greeter, greeting=\"Hello\")\n", "\n", "print(hier(\"brother\"))\n", "print(helloer(\"sir\"))" ] }, { "cell_type": "markdown", "id": "737347b9", "metadata": {}, "source": [ "Например, мы можем создать новую функцию `hier`, которая будет всегда приветствовать с помощью приветствия \"Hi\". Делается это с помощью функции `partial`, которая принимает, опять же, функцию и какой-то параметр, который нужно подменить. В данном случае у нас будет функция `hier` и функция `helloer`. `helloer` всегда приветствует с помощью \"Hallo\", а `hier` — всегда с помощью \"Hi\"." ] }, { "cell_type": "markdown", "id": "9c07a270", "metadata": {}, "source": [ "Например, у нас формальное и неформальное приветствие. Таким образом, у нас была функция `greeter`, которая принимает `greeting` и `person`, а стала только функция `hier` и `helloer`, которая принимает только `person`. Например, мы можем поздороваться неформально со своим братом и с каким-то незнакомым мужчиной с помощью \"Hallo\". Функция `partial` позволяет вам подменять определенные аргументы и модифицировать поведение функций. Например, мы можем определить логер, который записывает информацию только в какой-то конкретный файл или любые действия на ваш вкус." ] }, { "cell_type": "markdown", "id": "5fe2ecb1", "metadata": {}, "source": [ "Списочные выражения, очень важная концепция, которая используется практически повсеместно, потому, что позволяет вам упростить жизнь, и написать красивый код. Раньше, чтобы создать список, например, квадратов, мы с вами писали цикл. Создавали список и в цикле в этот список добавляли элементы — квадраты числа. Получалось довольно просто, однако нам необходимо определять список, определять цикл и в цикле ещё делать `append`.\n", "\n", "Это довольно многословно, в Python-е так не годится, и поэтому есть более простой, удобный метод написать то же самое. Этот метод называется \"списочное выражение\", или `list comprehensions`, которое выглядит так. У нас есть квадратные скобочки, и мы в квадратных скобочках пишем цикл. У нас цикл по `range`-у, мы берем `number`, этот `number` возводим в квадрат, и присваиваем его в список. Таким образом у нас список наполняется с помощью вот таких элементов. В цикле у нас добавляются квадраты числа и получается точно такой же результат — это называется \"списочное выражение\", оно работает немного быстрее, чем такой вот цикл и выглядит намного лаконичнее и короче. Поэтому практически всегда используются именно они, именно \"списочные выражения\"." ] }, { "cell_type": "code", "execution_count": 27, "id": "606fba9b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n" ] } ], "source": [ "square_list = []\n", "\n", "for number in range(10):\n", " square_list.append(number ** 2)\n", " \n", "print(square_list)" ] }, { "cell_type": "code", "execution_count": 28, "id": "e6076d78", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]\n" ] } ], "source": [ "square_list = [number ** 2 for number in range(10)]\n", "print(square_list)" ] }, { "cell_type": "markdown", "id": "2d240574", "metadata": {}, "source": [ "Точно так же можно написать списочное выражение с каким-то условием. А если нам нужны только четные числа в нашем списке, мы бы писали что-то в этом роде. У нас был бы цикл и условия, и мы только при выполнении условия бы добавляли в наш список элемент. Мы можем точно так же добавить условие в `list comprehensions`, и добавлять наш `num` только при выполнении условия остаток от деления равен нулю." ] }, { "cell_type": "code", "execution_count": 29, "id": "fa856e67", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 2, 4, 6, 8]\n" ] } ], "source": [ "even_list = []\n", "\n", "for number in range(10):\n", " if number % 2 == 0:\n", " even_list.append(number)\n", " \n", "print(even_list)" ] }, { "cell_type": "code", "execution_count": 30, "id": "d417cb66", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 2, 4, 6, 8]\n" ] } ], "source": [ "even_list = [num for num in range(10) if num % 2 == 0]\n", "print(even_list)" ] }, { "cell_type": "markdown", "id": "9625d939", "metadata": {}, "source": [ "Точно так же мы можем определять словари. Например, с помощью фигурных скобочек и двоеточия у нас соответственно в словаре, это ключ, и number в квадрате — это значение. Например, у нас строится отображение из числа в его квадрат. Мы можем определять точно так же с помощью `dict comprehensions` \"дикты\"." ] }, { "cell_type": "code", "execution_count": 31, "id": "e584a051", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}\n" ] } ], "source": [ "square_map = {number: number ** 2 for number in range(5)}\n", "print(square_map)" ] }, { "cell_type": "markdown", "id": "2edd7643", "metadata": {}, "source": [ "Мы можем делать с помощью обычных фигурных скобочек без двоеточий делать `set`-ы." ] }, { "cell_type": "code", "execution_count": 32, "id": "04d502db", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n" ] } ], "source": [ "reminders_set = {num % 10 for num in range(100)}\n", "print(reminders_set)" ] }, { "cell_type": "markdown", "id": "2cbba536", "metadata": {}, "source": [ "Обратите внимание, это опять же довольно мощные конструкции, которые здорово и легко использовать, однако, например, `list comprehensions`-ы позволяют вам вкладывать циклы `for` друг в друга и строить какие-то более сложные вещи.\n", "\n", "Например, вы можете в условиях писать сложные предикаты. " ] }, { "cell_type": "markdown", "id": "e276ea97", "metadata": {}, "source": [ "Пожалуйста не делайте этого, держите ваш код простым и понятным. Несмотря на то, что функциональные особенности языка очень легко использовать и писать довольно большие многострочные выражения, лучше, чтобы ваш код был простым и понятным. Не злоупотребляйте этим. Ну и что интересно, если мы просто без скобочек напишем такое выражение, то у нас тоже что-то получится. И на самом деле это генератор. О генераторах мы поговорим позже. Пока просто знайте, что это какой-то объект, в котором, например, можно итерироваться. Но это не список, не `tuple`, и не `set`." ] }, { "cell_type": "code", "execution_count": 33, "id": "4d02e541", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n" ] } ], "source": [ "print(type(number ** 2 for number in range(5)))" ] }, { "cell_type": "markdown", "id": "abb2a048", "metadata": {}, "source": [ "Ещё одна важная функция, которая часто рассматривается в контексте функционального программирования, это функция `zip`. Функция `zip` позволяет вам склеить два итерабельных объекта. В данном случае у нас есть `numList`, `squaredList`, представляющий собой квадраты чисел, и мы склеиваем их. И у нас первый элемент одного списка с первым элементом других списков попадает в один `tuple`. Происходит что-то в этом роде. Часто бывает функция эта полезна." ] }, { "cell_type": "code", "execution_count": 34, "id": "67c6fbd2", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25), (6, 36)]" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "num_list = range(7)\n", "squared_list = [x ** 2 for x in num_list]\n", "\n", "list(zip(num_list, squared_list))" ] }, { "cell_type": "markdown", "id": "53cb437e", "metadata": {}, "source": [ "Итак. Мы с вами разобрали такую страшную концепцию, как функциональное программирование, которая на самом деле проста.\n", "\n", "Мы с вами узнали, что функции - это объекты первого класса, их можно передавать внутри функций, возвращать из функций, создавать внутри функций, возвращать, то есть работать с ними как с обычными объектами; разобрали с вами классические функции `map`, `filter` и `reduce`; посмотрели на анонимные функции, которые можно создавать `in place` и разобрали списочные выражения, которые часто вам могут упростить жизнь для того, чтобы создавать списки, `set`-ы, `dict`-ы, или что-то в этом роде." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.12" } }, "nbformat": 4, "nbformat_minor": 5 }