{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d8cf2624",
   "metadata": {},
   "source": [
    "# Функции #"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f57336b3",
   "metadata": {},
   "source": [
    "Привет. На этой лекции мы с вами поговорим о функциях и о том, как с ними работать в языке Python.\n",
    "\n",
    "Скорее всего, вы уже встречались с функциями в других языках программирования и знаете, что функция - это просто блок кода, который можно переиспользовать несколько раз в разных местах программы.\n",
    "\n",
    "Мы можем вызывать функцию с какими-то аргументами и получать значения обратно.\n",
    "\n",
    "Чтобы определить функцию в языке Python, нужно использовать литерал `def` и с помощью отступа определить блок кода функции. В данном случае мы определяем функцию `get_seconds`, которая просто возвращает количество секунд на данный момент."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "8cb5198b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "32"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from datetime import datetime\n",
    "\n",
    "def get_seconds():\n",
    "    \"\"\"Return current seconds\"\"\"\n",
    "    return datetime.now().second\n",
    "\n",
    "get_seconds()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "11105ce4",
   "metadata": {},
   "source": [
    "Обратите внимание, функцию я определяю с помощью нижнего подчеркивания по `PEP8`. А дальше у функции может идти документационная строка, которая описывает то, что, собственно, внутри функции происходит. Чтобы вернуть значения из функции, мы используем `return`, в данном случае возвращаем количество секунд, а можем не писать `return`, и тогда по умолчанию вернется `None`."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b58ccdf2",
   "metadata": {},
   "source": [
    "Чтобы вызвать функцию, нужно использовать круглые скобочки, и если нужно, передать туда параметры. Чтобы получить документационную строку, можно обратиться к атрибуту `__doc__`, а имя функции получается с помощью атрибута `__name__`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "9a6d8edf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Return current seconds'"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "get_seconds.__doc__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b148cbf7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'get_seconds'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "get_seconds.__name__"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e378fc9d",
   "metadata": {},
   "source": [
    "Чаще всего функция определяется именно с параметрами, потому что нам важно передать какие-то значения внутри функции и работать уже с ними. Мы определяем функцию `split_tags`, которая принимает какой-то `tag_string`, в данном случае это строка с тегами, например, это `python`, `perl` и `pascal`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5ad43014",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['python', 'perl', 'pascal']"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def split_tags(tag_string):\n",
    "    tag_list = []\n",
    "    \n",
    "    for tag in tag_string.split(\",\"):\n",
    "        tag_list.append(tag.strip())\n",
    "        \n",
    "    return tag_list\n",
    "\n",
    "split_tags(\"python, perl, pascal\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a17c8dec",
   "metadata": {},
   "source": [
    "Мы хотим разбить эту строку по запятым и вернуть список тегов. Эта функция, собственно, это и делает. Мы передаем в нашу функцию строку и получаем обратно список. Если мы попытаемся вызвать эту функцию без параметров, у нас выпадет ошибка `TypeError`, потому что функция ожидает параметр и не знает, что делать, если его нет."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "cc8143dd",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "split_tags() missing 1 required positional argument: 'tag_string'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-5-459911d1e387>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0msplit_tags\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: split_tags() missing 1 required positional argument: 'tag_string'"
     ]
    }
   ],
   "source": [
    "split_tags()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96584d83",
   "metadata": {},
   "source": [
    "Как вы могли заметить, мы не указываем явно, какого типа параметры функция ожидает, потому что Python - это динамический язык.\n",
    "\n",
    "Если вы уже сталкивались со статически типизированными языками вроде C, вы могли видеть, что, например, в C типы аннотируются, мы явно указываем, какого типа должен быть параметр функции и какого типа возвращаемые значения.\n",
    "\n",
    "[Aннотация типов](Aннотация%20типов.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6edf6fa1",
   "metadata": {},
   "source": [
    "Как же передаются параметры в наши функции? Например, в статических языках, опять же, вроде C, проводится очень четкое различие между передачей по ссылке и по значению.\n",
    "\n",
    "В Python'е каждая переменная является связью имени с объектом в памяти. И именно это имя, именно эта ссылка на объект передается в функцию."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a080fbf3",
   "metadata": {},
   "source": [
    "Таким образом, если мы определим функцию `extender`, которая принимает какой-то `source_list`, то есть исходный `list`, и новый `list` и пытается расширить `source_list` с помощью `extend_list`'а, то есть просто добавить в конец все его элементы, мы вот определяем `values` и пытаемся расширить `values` с помощью `[4, 5, 6]`, то окажется, что `values` у нас изменится."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "65c8b6ed",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 2, 3, 4, 5, 6]\n"
     ]
    }
   ],
   "source": [
    "def extender(source_list, extend_list):\n",
    "    source_list.extend(extend_list)\n",
    "    \n",
    "values = [1, 2, 3]\n",
    "extender(values, [4, 5, 6])\n",
    "\n",
    "print(values)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "883ff05c",
   "metadata": {},
   "source": [
    "Что же произошло? Наша ссылка на объект в памяти попала в `extender`. И так как `list` является изменяемым объектом, мы можем изменить этот `list`, и объект в памяти изменился. Наш `values` в глобальном `scope` поменял свое значение. Если мы попытаемся как-то изменить неизменяемое значение, в данном случае `tuple`, то у нас ничего не получится, потому что мы передаем ссылку на объект в памяти, но объект является неизменяемым."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "2f508089",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "('Guido', '31/01')\n"
     ]
    }
   ],
   "source": [
    "def replacer(source_tuple, replace_with):\n",
    "    source_tuple = replace_with\n",
    "    \n",
    "user_info = (\"Guido\", \"31/01\")\n",
    "replacer(user_info, (\"Larry\", \"27/09\"))\n",
    "\n",
    "print(user_info)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06ae74a9",
   "metadata": {},
   "source": [
    "В данном случае у нас `user_info` осталось неизменным. Однако стоит быть внимательным с изменением каких-то глобальных переменных внутри функции. Это является плохим тоном, и не стоит так программировать, потому что часто бывает не очевидно, если вы вызываете какую-то функцию, а объект в глобальном `scope` изменяется. Используйте возвращаемое значение и не путайте других программистов."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0ff0ab4f",
   "metadata": {},
   "source": [
    "В Python'е также существуют именованные аргументы, которые иногда бывают полезны. Например, мы можем определить функцию `say`, которая принимает два аргумента — `greeting` и `name`, просто приветствует какого-то человека.\n",
    "\n",
    "Однако мы можем также передать эти параметры в другом порядке, проименовав их с помощью `name` и `greeting`. Таким образом, мы передаем вначале имя, а потом `greeting`, тем не менее у нас всё работает точно так же."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "2580a8b3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello Kitty!\n",
      "Hello Kitty!\n"
     ]
    }
   ],
   "source": [
    "def say(greeting, name):\n",
    "    print(f\"{greeting} {name}!\")\n",
    "    \n",
    "say(\"Hello\", \"Kitty\")\n",
    "say(name=\"Kitty\", greeting=\"Hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0bbd0c13",
   "metadata": {},
   "source": [
    "Немножко про область видимости. Важно понимать, что переменные, объявленные вне области видимости функции, нельзя изменять. В данном случае у нас есть глобальная переменная `result`, и мы пытаемся прибавить к ней внутри функции какое-то значение. Мы пытаемся к `result` прибавить единичку при вызове функции `increment`. У нас ничего не получается, падает ошибка, потому что мы не можем внутри функции изменять объекты из глобальной области видимости."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "1b46f831",
   "metadata": {},
   "outputs": [
    {
     "ename": "UnboundLocalError",
     "evalue": "local variable 'result' referenced before assignment",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mUnboundLocalError\u001b[0m                         Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-20-b35f2869d543>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[0;32m      5\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 7\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mincrement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;32m<ipython-input-20-b35f2869d543>\u001b[0m in \u001b[0;36mincrement\u001b[1;34m()\u001b[0m\n\u001b[0;32m      2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      3\u001b[0m \u001b[1;32mdef\u001b[0m \u001b[0mincrement\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m     \u001b[0mresult\u001b[0m \u001b[1;33m+=\u001b[0m \u001b[1;36m1\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      5\u001b[0m     \u001b[1;32mreturn\u001b[0m \u001b[0mresult\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      6\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mUnboundLocalError\u001b[0m: local variable 'result' referenced before assignment"
     ]
    }
   ],
   "source": [
    "result = 0\n",
    "\n",
    "def increment():\n",
    "    result += 1\n",
    "    return result\n",
    "\n",
    "print(increment())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc5fa177",
   "metadata": {},
   "source": [
    "И очень важно это соблюдать опять же по той же самой причине. Если мы внутри функции изменяем какие-то глобальные объекты, очень часто это не очевидно. У нас есть глобальная переменная, и вызов каких-то функций меняет ее значение. Совершенно непонятно, что это происходит и часто приводит к запутанному коду."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a903105",
   "metadata": {},
   "source": [
    "Существует в Python'е возможность изменять глобальные переменные с помощью `global`, например, или `non local`, но я не рекомендую вам использовать эти особенности."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "f60a05d7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n"
     ]
    }
   ],
   "source": [
    "result = 0\n",
    "\n",
    "def increment():\n",
    "    global result\n",
    "    \n",
    "    result += 1\n",
    "    return result\n",
    "\n",
    "print(increment())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fe3001d",
   "metadata": {},
   "source": [
    "Существует также возможность использовать аргументы по умолчанию вашей функции. Часто у вас бывают какие-то аргументы, которые можно передавать, а можно не передавать. И у них, у этих аргументов, могут быть какие-то дефолтные значения. В данном случаем мы можем приветствовать человека, если передано имя, а можем просто вывести какую-то дефолтную фразу."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "28ec1b28",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello, it's me...\n"
     ]
    }
   ],
   "source": [
    "def greeting(name=\"it's me...\"):\n",
    "    print(f\"Hello, {name}\")\n",
    "    \n",
    "greeting()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be47e975",
   "metadata": {},
   "source": [
    "Однако, стоит быть внимательным с аргументами по умолчанию, если мы используем в качестве аргументов по умолчанию изменяемые значения. Обратите внимание на пример.\n",
    "\n",
    "У нас есть функция `append_one`, и в качестве дефолтного значения, значения по умолчанию `iterable`, у нас используется список.\n",
    "\n",
    "Собственно `append_one` просто прибавляет единичку к переданному списку или дефолтному списку. То есть у нас возвращается либо наш исходный список плюс один, либо просто список из единички. По крайней мере так мы ожидаем."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "4723d072",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1, 1]\n"
     ]
    }
   ],
   "source": [
    "def append_one(iterable=[]):\n",
    "    iterable.append(1)\n",
    "    return iterable\n",
    "\n",
    "print(append_one([1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "abbde05d",
   "metadata": {},
   "source": [
    "Если мы передадим в `append_one` список из единицы, нам вернется две единички, что мы и ожидаем. Однако что же произойдет, если мы вызовем `append_one` два раза?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "84b2ef43",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1]\n",
      "[1, 1]\n"
     ]
    }
   ],
   "source": [
    "print(append_one())\n",
    "print(append_one())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db6f038f",
   "metadata": {},
   "source": [
    "Вначале, как мы и ожидаем, к нам вернется единичка, потому что мы взяли наше дефолтное значение, добавили единичку в список и вернули. Однако если мы вызовем во второй раз, окажется, что у нас уже две единички, хотя мы явно ожидаем одну.\n",
    "\n",
    "Чтобы понять, почему это так, можно посмотреть на дефолтное значение нашей функции, и окажется, что там уже содержатся эти самые две единички. Почему так происходит?\n",
    "\n",
    "При определении функции, когда интерпретатор Python'а бежит по вашему файлу с кодом, определяется связь между именем функции и дефолтными значениями. Таким образом, у каждой функции появляется словарь, появляется `tuple` какой-то с дефолтными значениями. И именно в эти переменные каждый раз и происходит запись. Таким образом, если дефолтные значения являются изменяемыми, в них можно записывать, потому что это обычные переменные."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "id": "88a2eab2",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "([1, 1],)\n"
     ]
    }
   ],
   "source": [
    "print(append_one.__defaults__)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da7c8a62",
   "metadata": {},
   "source": [
    "Что же нужно делать в таком случае? Нужно определять дефолтные значения как `None`. И если нам не передан какой-то параметр, мы просто создаем новый список на лету. Можно это делать двумя механизмами."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "166799cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "def function(iterable=None):\n",
    "    if iterable is None:\n",
    "        iterable = []\n",
    "        \n",
    "def function(iterable=None):\n",
    "    iterable = iterable or []"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fe32e50c",
   "metadata": {},
   "source": [
    "Очень красивой особенностью Python'а является возможность определения функции, которая принимает разные количества аргументов.\n",
    "\n",
    "Мы можем определить функцию `printer`, которая принимает разное количество аргументов. Может быть один, два, три, четыре, сотня аргументов.\n",
    "\n",
    "В данном случае все аргументы записываются в `tuple args`. И мы видим, что это действительно `tuple`, если мы выведем тип. И мы можем, например, эти аргументы как-то потом использовать, можем просто по ним как-то пробежаться и вывести их."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "5a5112e7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "1\n",
      "2\n",
      "3\n",
      "4\n",
      "5\n"
     ]
    }
   ],
   "source": [
    "def printer(*args):\n",
    "    print(type(args))\n",
    "    \n",
    "    for argument in args:\n",
    "        print(argument)\n",
    "\n",
    "printer(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2552db47",
   "metadata": {},
   "source": [
    "Точно так же можно разворачивать наш список в аргументах. Мы можем определить наш список с Джоном, Биллом и Эми и передать наш список как аргументы в нашу функцию."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "b458e59b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'tuple'>\n",
      "John\n",
      "Bill\n",
      "Amy\n"
     ]
    }
   ],
   "source": [
    "name_list = [\"John\", \"Bill\", \"Amy\"]\n",
    "printer(*name_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a2299cf",
   "metadata": {},
   "source": [
    "Таким образом, первым аргументом станет Джон, вторым — Билл, и третьим — Эми.\n",
    "\n",
    "Это так называемая распаковка списка. Есть еще подобные способы использования:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "21a58d1d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 [3, 4, 5]\n"
     ]
    }
   ],
   "source": [
    "a, b, *others = [1, 2, 3, 4, 5]\n",
    "\n",
    "print(a, b, others)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "1bdc2fa4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1 2 [3, 4] 5\n"
     ]
    }
   ],
   "source": [
    "a, b, *others, c = [1, 2, 3, 4, 5]\n",
    "\n",
    "print(a, b, others, c)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cf3a0381",
   "metadata": {},
   "source": [
    "Точно так же это работает в случае со словарями, в данном случае мы можем определить функцию `printer`, которая принимает разное количество именованных аргументов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "8ade7f94",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'dict'>\n",
      "a 10\n",
      "b 11\n"
     ]
    }
   ],
   "source": [
    "def printer(**kwargs):\n",
    "    print(type(kwargs))\n",
    "    for key, value in kwargs.items():\n",
    "        print(f\"{key} {value}\")\n",
    "        \n",
    "printer(a=10, b=11)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5a455966",
   "metadata": {},
   "source": [
    "Собственно, все записывается в `kwargs`, и таким образом `kwargs` у нас останется `dict`'ом. Если мы передадим два именованных аргумента, `a = 10` и `b = 11`, то у нас получится словарь, и мы можем потом этот словарь как-то использовать, используя параметры и, например, просто их выводя на экран.\n",
    "\n",
    "Точно так же мы можем разыменовывать, разворачивать эти словари в обратную сторону. Таким образом, если у нас есть словарь, мы можем передавать значения из этого словаря как аргументы, именованные, в нашу функцию. Таким образом, когда мы используем две звездочки при вызове функции, у нас первым параметром становится `user_id`, а вторым параметром становится `feedback`. Это используется практически везде и позволяет вам определять очень гибкие функции, которые принимают различное количество аргументов — именованных и позиционных."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "d4989f77",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'dict'>\n",
      "user_id 117\n",
      "feedback {'subject': 'Registration fields', 'message': 'There is no country for old men'}\n"
     ]
    }
   ],
   "source": [
    "payload = {\n",
    "    \"user_id\": 117,\n",
    "    \"feedback\": {\n",
    "        \"subject\": \"Registration fields\",\n",
    "        \"message\": \"There is no country for old men\"\n",
    "    }\n",
    "}\n",
    "\n",
    "printer(**payload)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
