You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

910 lines
39 KiB
Plaintext

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{
"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": [
"<class 'generator'>\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
}