{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Базовые типы: строки и байтовые строки #"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На этой лекции мы с вами продолжаем знакомство с базовыми типами и сейчас мы рассмотрим такой тип как строки и байтовые строки. Начнем мы со строк.\n",
    "\n",
    "Строка в Python это неизменяемая последовательность unicod'ных символов. Давайте посмотрим на примере. Чтобы определить строку, всё что нам нужно сделать, это записать её и обрамить в двойные кавычки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "да будет слово ваше\n"
     ]
    }
   ],
   "source": [
    "example = \"да будет слово ваше\"\n",
    "print(example)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Убедимся что тип, объект, который мы только что с вами создали это str, то есть строка. Опять же, мы пользуемся встроенной функцией type для этого. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'str'>\n"
     ]
    }
   ],
   "source": [
    "print(type(\"слово\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Иногда может получится так, что вам внутри строки может записать слова которые включают в себя двойные кавычки. В таком случае вы можете воспользоватся одинарными кавычками для того, чтобы записать строку."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "да будет \"слово\" ваше\n"
     ]
    }
   ],
   "source": [
    "example = 'да будет \"слово\" ваше'\n",
    "print(example)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также можно двойные кавычки экранировать символом обратного слэша и таким образом мы можем двойные кавычки записывать внутри строки, которая создается с помощью двойных кавычек."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "да будет \"слово\" ваше\n"
     ]
    }
   ],
   "source": [
    "example = \"да будет \\\"слово\\\" ваше\"\n",
    "print(example)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также в Python'е есть концепция сырых строк. Это строки, которые начинаются с английской буквы `r`. Посмотрите на пример. Мы можем с помощью сырых строк объявить строку и не экранировать специальные символы, которые находятся внутри этой строки. В первом случаи мы экранируем обратные слэши обратными слэшами и это выглядит достаточно некрасиво. Во втором случае мы используем сырые строки и просто записываем наш путь на диске."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "C:\\Windows\n"
     ]
    }
   ],
   "source": [
    "path = \"C:\\\\Windows\"\n",
    "print(path)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "C:\\Windows\n"
     ]
    }
   ],
   "source": [
    "path = r\"C:\\Windows\"\n",
    "print(path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Разбить длинную строку внутри вашего кода на несколько строк можно с помощью обратного слэша. Вы записываете строку, ставите обратный слэш и переносите строчку на следующую строку кода, как видно на примере."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Perl - это тот язык, который одинаково выглядит как до, так и после RSA шифрования. Кейт Бостик\n"
     ]
    }
   ],
   "source": [
    "example = \"Perl - это тот язык, который одинаково \" \\\n",
    "          \"выглядит как до, так и после RSA шифрования. \" \\\n",
    "          \"Кейт Бостик\"\n",
    "\n",
    "print(example)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также есть возможность записывать строки, состоящие из нескольких абзацов, какой-то текст возможно. Это делается с помощью тройных кавычек."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 75,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Есть всего два типа языков программирования: те, на которые\n",
      "люди всё время ругаются, и те, которые никто не использует.\n",
      "\n",
      "Бьерн Страуструп\n",
      "\n"
     ]
    }
   ],
   "source": [
    "example = \"\"\"\n",
    "Есть всего два типа языков программирования: те, на которые\n",
    "люди всё время ругаются, и те, которые никто не использует.\n",
    "\n",
    "Бьерн Страуструп\n",
    "\"\"\"\n",
    "\n",
    "print(example)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как объединить две строки в одну? Иногда вам требуется объединить две строки. Это делается просто с помощью оператора сложения, с помощью знака \"+\" и в итоге получается новая строка."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 76,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Можно просто складывать строки'"
      ]
     },
     "execution_count": 76,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"Можно просто \" + \"складывать строки\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Когда вы складываете много строчек, это может быть не еффективно, потому что строки неизменяемые и при сложении нескольких строк на каждое сложение создается новый объект строковый и создается новая локация памяти. Однако в самом простейшем случае вы можете просто сложить две строки и это будет решением множества практических задач. Строки можно даже умножать, потому что для них переопределен оператор умножения. Если вы умножите строчку на три, например, то получите ту же самую строчку, повторяющуюся три раза."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 80,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "бла бла бла \n"
     ]
    }
   ],
   "source": [
    "print(\"бла \" * 3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Я сказал, что строки неизменяемы. Давайте посмотрим на это подробно. На примере на слайде мы создаем строку. Дальше мы печатаем её адрес в памяти. Адрес памяти у объекта в Python можно запросить с помощью встроеной функции `id`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 77,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1583375738000\n",
      "1583375738336\n"
     ]
    }
   ],
   "source": [
    "example = \"Привет\"\n",
    "print(id(example))\n",
    "example += \", Мир!\"\n",
    "print(id(example))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Далее мы к строке добавляем ещё одну строчку, чтобы у нас получилась строка \"Привет мир\" и печатаем адрес получившегося строкового объекта. Как мы видим, эти адреса отличаются. То есть когда мы изменили нашу начальную строку, мы на самом деле создали новый строковый объект. Обратите внимание, что я говорю строковый объект. Про объектную структуру в Python мы с вами будем говорить в отдельном видео. Я по ходу наших лекций, стараюсь их подчеркнуть, что всё в Python является объектом. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Следующая особенность это срезы строк. Срезы строк очень удобны для того, чтобы из целой строки выделить какую-то подстроку. Синтаксис срезов лучше всего посмотреть на примере. Давайте посмотрим пример, это всё та же строка \"Привет, Мир!\"."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Далее мы берем от нее срез. Срез это - квадратные скобки и внутри три параметра, start, stop, step. То есть, начиная с какого символа мы хотим получить подстроку, до какого символа и какой шаг. На примере мы видим, что мы берем из строки срез начиная с восьмого символа и до конца. Отсутствие параметров stop, step говорит как раз, что нам нужно брать срез до конца."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 81,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Мир!'"
      ]
     },
     "execution_count": 81,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example = \"Привет, Мир!\"\n",
    "example[8:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На втором примере мы берем срез с 8 по 11 символы."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Мир'"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example[8:11]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также в срезе можно использовать отрицательные значения. Если вы посмотрите на пример, где берется срез -4:. Это означает что срез начнется с четвертого символа с конца строки, дальше двоеточие и до конца строки. Мы в итоге получаем слово \"Мир!\" из нашей строки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Мир!'"
      ]
     },
     "execution_count": 84,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example[-4:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Использование step. Использование шага может быть оправдано тогда, когда вам нужно получить только символы из строки с определенным шагом. Давайте посмотрим на пример. У нас есть строка, которая состоит из цифр от 0 до 9, и мы хотим получить подстроку, которая состоит из четных чисел. В данном случае step нам очень поможет и если мы возьмем срез с шагом 2, то получим то, что мы хотим. Обычно шаг используется на практике для того, чтобы инвертировать строку, то есть развернуть её в обратном направлении. Например, у нас есть строка \"Москва\" и мы берем срез:-1, то есть шаг = -1, то мы получаем строку развернутую."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'02468'"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example = \"0123456789\"\n",
    "example[::2]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'авксоМ'"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example = \"Москва\"\n",
    "example[::-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Так как Python всё есть объект, у строк есть методы. Что это значит? Если мы определим строку и присвоим её переменой, то у этой строки мы потом можем вызывать различные методы. Например на примере мы видим вызов метода сount строки, который позволяет найти количество вхождений буквы \"о\" в строку."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 89,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 89,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "torvalds = \"\"\"\n",
    "Болтовня ничего не стоит. Покажите мне код\n",
    "Linus Torvalds\n",
    "\"\"\"\n",
    "\n",
    "torvalds.count(\"о\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Давайте посмотрим на другой метод, который называется capitalize. Этот метод позволяет сделать первую букву в строке заглавной."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Москва'"
      ]
     },
     "execution_count": 91,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example = \"москва\"\n",
    "example.capitalize()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ну и последнее, на что мы посмотрим, это isdigit. Isdigit позволяет проверить является ли строка, у которой мы вызвали этот метод, числом и возвращает boolean - True или False."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 92,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 92,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"2021\".isdigit()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На самом деле существует ещё масса методов у строк, которые вы можете посмотреть в документации и с которыми вы столкнетесь на практике и в том числе в рамках нашего курса. Мы ещё будем пользоватся методами строк активно. https://docs.python.org/3/library/string.html\n",
    "\n",
    "Давайте пойдем дальше и посмотрим на оператор in, который позволяет проверить наличие подстроки в строке. Например, мы можем записать вот такую вещь и проверить, что 3.14 содержится в строке, которая написана на экране. В данном случае она содержится, поэтому результат True, то есть истина."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 95,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 95,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"3.14\" in \"14 марта отметят День π: если записать дату на американский манер, получится 3.14. Точное значение числа вычислить невозможно, поэтому у него тянется громадный хвост цифр.\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если мы проверим наличие подстроки, которая не содержится в строке, мы получаем False. Это очень читаемо, это очень удобно и это идиоматично."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 98,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"Rossum\" in  \"Гвидо ван Россум\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также есть оператор `for…in`. Выражение `for…in`, которое позволяет итерироватся по строке. Как мы знаем, строка это последовательность. Последовательность юникодных символов. Соответственно итерация по строке означает взятие каждого следующего элемента по очереди. Давайте посмотрим как это сделать. Если мы объявим строковый объект изначальный \"привет\" и дальше проитерируемся по этой строке, используя выражение `for…in`, то мы можем получить каждый отдельный символ этой строки - букву П, букву Р, букву И и т.д."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 99,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Буква п\n",
      "Буква р\n",
      "Буква и\n",
      "Буква в\n",
      "Буква е\n",
      "Буква т\n"
     ]
    }
   ],
   "source": [
    "example = \"привет\"\n",
    "\n",
    "for letter in example:\n",
    "    print(\"Буква\", letter)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Конвертация типов. Давайте посмотрим, что у нас можно сделать со строкой, например с классом `str`. Например, у нас есть вещественное число, и мы можем в runtime, в процесе работы Python применить конвертацию типов и преобразовать вещественное число в число типа `str`. И на примере мы это видим. Мы берем `999.01`, преобразовываем в строку и проверяем, что у нас действительно получилась в результате конвертации типов строка. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 102,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'str'>\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'999.01'"
      ]
     },
     "execution_count": 102,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "string = str(999.01)\n",
    "\n",
    "print(type(string))\n",
    "string"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если мы попробуем преобразовать не пустую строку к логическому типу, то мы получим `True`. Пустая строка, которая не содержит ни одного символа в себе, при конвертации к логическому типу возвращает `False`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 103,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bool(\"Строка\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bool(\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Очень часто на практике может возникнуть задача форматирования строк. Что такое \"форматирование\"? Форматирование строк - это процесс, когда у вас есть какой-то шаблон строковый, внутрь которого вместо определенных placeholder-ов вы хотите подставить те или иные значения из ваших переменных, например. Давайте посмотрим на первый способ форматирования в Python, которых достаточно много, на самом деле. Первый способ это вот такой, как вы видите на примере, мы placeholder-ы определяем с помощью синтаксиса %s. Это означает, что вместо этого placeholder-а мы можем подставить строку. Если бы мы хотели подставить вместо него целое число, то это был бы %d. И дальше, с помощью оператора % мы можем заменить эти placeholder-ы на наши значения. На самом деле, это достаточно богатая спецификация всевозможных placeholder-ов и про все можно почитать в документации."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Лень — главное достоинство программиста. Larry Wall'"
      ]
     },
     "execution_count": 105,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "template = \"%s — главное достоинство программиста. %s\"\n",
    "template % (\"Лень\", \"Larry Wall\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 113,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'14 марта отметят День π: если записать дату на американский манер, получится 3.14.'"
      ]
     },
     "execution_count": 113,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "template = \"%d марта отметят День π: если записать дату на американский манер, получится %.2f.\"\n",
    "template % (14, 3.14)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Мы давайте посмотрим на другой чуть более удобный способ, на мой взгляд, форматирования строк - это использование метода `format`, который есть у строки. В данном случае placeholder-ом являются фигурные скобки. Мы пишем строку, которая включает в себя placeholder-ы, а дальше используем метод `format`, которому передаем значения, которые мы должны подставить вместо placeholder-ов. В данном случае нам необязательно указывать тип внутри наших placeholder-ов, Python все это разрешит сам."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 110,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Цифры не лгут, но лжецы пользуются формулами. (Роберт Хайнлайн)'"
      ]
     },
     "execution_count": 110,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"{} не лгут, но {} пользуются формулами. ({})\".format(\n",
    "    \"Цифры\", \"лжецы\", \"Роберт Хайнлайн\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Другой способ, чуть более наглядный, опять же с использованием функции `format`, но с именованными аргументами. В данном случае, у нас внутри placeholder-а содержится имя этих placeholder-ов, а в функцию `format` мы передаем, соответственно, именованные аргументы по имени, совпадающему с именем placeholder-а и получаем итоговую строку."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 111,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'640 Кб должно быть достаточно для каждого. (Билл Гейтс)'"
      ]
     },
     "execution_count": 111,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"{num} Кб должно быть достаточно для каждого. ({author})\".format(\n",
    "    num=640, author=\"Билл Гейтс\"\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Но, пожалуй, самый удобный способ форматирования строк в Python появился в Питоне 3.6. Это так называемые f-строки. Это строки, которые начинаются с префикса английской буквы f. Посмотрите на пример - мы можем просто объявить две переменные, а затем записать f-строку, в которой внутри placeholder-ов просто написать имена этих переменных. И вместо этих placeholder-ов будут подставлены правильные значения."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 115,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Преждевременная оптимизация — корень всех зол. (Дональд Кнут)'"
      ]
     },
     "execution_count": 115,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "subject = \"оптимизация\"\n",
    "author = \"Дональд Кнут\"\n",
    "\n",
    "f\"Преждевременная {subject} — корень всех зол. ({author})\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Также есть возможность использовать модификаторы форматирования. Модификаторы форматирования позволяют вам сказать языку, как вы хотите то или иное значение в placeholder-е подставлять, в каком виде его в итоге вывести. Опять же, проще всего посмотреть на примере. Если у нас есть число и мы хотим вывести его в итоге на экран в двоичном представлении, мы можем воспользоваться модификатором форматирования `:#b`. И в итоге получаем на экране число в двоичном представлении."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 117,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Bin: 0b1000'"
      ]
     },
     "execution_count": 117,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "num = 8\n",
    "f\"Bin: {num:#b}\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Другой распространенный пример, когда нам нужен модификатор форматирования, это, например, деление, которое возвращает число с очень длинной десятичной частью. Например, 2/3, мы знаем, что это будет 0.(6). Если мы хотим вывести только три знака после точки, мы можем воспользоваться модификатором форматирования `:.3f`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.6666666666666666\n",
      "0.667\n"
     ]
    }
   ],
   "source": [
    "num = 2/3\n",
    "print(num)\n",
    "\n",
    "print(f\"{num:.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Опять же, модификаторов форматирования очень много и вы можете почитать про них подробно в документации. https://docs.python.org/3/library/string.html#format-specification-mini-language"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Встроенная функция input позволяет получить ввод пользователя в виде строки. Пожалуй, лучше всего это показать на интерактивном примере в консоли. Давайте мы это сейчас и сделаем. В консоли мы запустим Python 3 `name = input(\"введите ваше имя\")`. После этого нам предлагается ввести наше имя. Давайте его введем. И после того, как мы ввели имя, нажали `enter`, внутри переменной `name` содержится как раз то значение, которое мы ввели, и мы можем с ним работать."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "metadata": {},
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "введите ваше имя Саша\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Привет, Саша!\n"
     ]
    }
   ],
   "source": [
    "name = input(\"введите ваше имя\")\n",
    "print(f\"Привет, {name}!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Следующее, на что мы посмотрим, это новый тип, это байтовые строки, тип `bytes`.\n",
    "\n",
    "Байты, как вы знаете, это минимальная единица хранения и передачи информации. Мы не можем, например, передавать по сети строки в виде юникодных символов. Нам нужно преобразовывать строки сначала в байты. То же самое, если мы хотим сохранить строку на диск, то диск работает с байтами. Нам нужно преобразовать наши юникодные символы в байты для этой операции. В Python для того, чтобы объявить байтовую строку используется литерал `b`. Давайте посмотрим на примере. Чтобы объявить байтовую строку мы используем литерал `b` и передаем в нее строчку \"hallo\". В данном случае мы получаем байтовую строку и она представляет собой последовательность чисел от 0 до 255. У нас получилось это сделать, потому что мы передали в литерал байтовой строки ASCII символы. То есть, символы, которые имеют свой номер в таблице ASCII от 0 до 255 и, соответственно, преобразовываются в байты очень легко. И давайте удостоверимся, что мы действительно получили байтовую строку. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'bytes'>\n"
     ]
    }
   ],
   "source": [
    "example = b\"hello\"\n",
    "print(type(example))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Проитерируемся по нашей байтовой строке. Так как она является последовательностью, то мы можем по ней итерироваться и мы это уже умеем делать, на примере строк было показано. И мы видим, что наша байтовая строка содержит числа от 0 до 255, однако, если мы попробуем преобразовать в байты нашу юникодную строку, которая, в данном случае \"привет\", мы получим ошибку."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 125,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "104\n",
      "101\n",
      "108\n",
      "108\n",
      "111\n"
     ]
    }
   ],
   "source": [
    "for element in example:\n",
    "    print(element)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 126,
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "bytes can only contain ASCII literal characters. (<ipython-input-126-2bb6ba7a62f0>, line 1)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  File \u001b[1;32m\"<ipython-input-126-2bb6ba7a62f0>\"\u001b[1;36m, line \u001b[1;32m1\u001b[0m\n\u001b[1;33m    example = b\"Привет\"\u001b[0m\n\u001b[1;37m              ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m bytes can only contain ASCII literal characters.\n"
     ]
    }
   ],
   "source": [
    "example = b\"Привет\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Как раз потому, что байты - это числа от 0 до 255, и нам нужно юникодные символы преобразовать в байты. Для этого используются кодировки. Самая известная кодировка - это UTF-8, поэтому мы сейчас на примере покажем, как обычную строку, в данном случае строку \"привет\", преобразовать в байты, используя кодировку UTF-8. Давайте создадим строковый объект, убедимся, что это строка. А дальше воспользуемся методом `encode`, который есть у строки. Encode принимает опциональным параметром кодировку, в которую мы хотим закодировать нашу строку. В данном случае UTF-8. В принципе, это кодировка по умолчанию, поэтому мы могли бы просто использовать метод `encode` без каких-то дополнительных аргументов. В результате мы видим, что мы получаем байтовую строку, то есть, последовательность байт."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 129,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "b'\\xd0\\xbf\\xd1\\x80\\xd0\\xb8\\xd0\\xb2\\xd0\\xb5\\xd1\\x82'\n",
      "<class 'bytes'>\n"
     ]
    }
   ],
   "source": [
    "example = \"привет\".encode(encoding=\"utf-8\")\n",
    "print(example)\n",
    "print(type(example))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Давайте посмотрим. Python напечатал на экран байтовую строку в виде последовательности шестнадцатиричных символов. Вначале мы видим символы `\\xd0\\xbf`. Что это такое? На самом деле, это представление маленькой кириллической буквы \"п\" в кодировке UTF.\n",
    "\n",
    "| Буква | Кодировка | hex   | dec (bytes) | dec   | binary            |\n",
    "|:------|:----------|:------|:------------|:------|:------------------|\n",
    "| п     | UTF-8     | D0 BF | 208 191     | 53439 | 11010000 10111111 |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Если мы посмотрим в шестнадцатиричном представлении как выглядит буква \"п\" в кодировке UTF, мы как раз видим эти шестнадцатиричные символы `\\xd0 \\xbf`. То есть, ничего магического. То же самое с другими буквами. Чтобы декодировать байты обратно в строку, мы можем воспользоваться методом строки, методом `decode`, который есть у байтов. Вызываем метод decode и получаем обратно UTF строку. Decode может принимать параметром кодировку, которую мы будем использовать. Но, так как мы работаем в данном случае с `UTF-8`, это значение по умолчанию, мы просто пишем `decode` без каких-то аргументов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 132,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'привет'"
      ]
     },
     "execution_count": 132,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "example.decode()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "На этой лекции мы научились работать со строками, посмотрели на основные операции, которые есть у нас для работы со строками, а также посмотрели на байтовые строки, на тип bytes. Вам на практике часто придется с ними сталкиваться.\n",
    "\n",
    "[Далее...](Объект%20None.ipynb)"
   ]
  }
 ],
 "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": 4
}
