{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Основы программирования в Python\n", "\n", "*Алла Тамбовцева, НИУ ВШЭ*\n", "\n", "*План лекции основан на [лекциях](http://math-info.hse.ru/2017-18/%D0%9F%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%B4%D0%BB%D1%8F_%D0%B4%D0%B0%D1%82%D0%B0-%D0%B6%D1%83%D1%80%D0%BD%D0%B0%D0%BB%D0%B8%D1%81%D1%82%D0%B8%D0%BA%D0%B8) Щурова И.В., курс «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ).*\n", "\n", "## Методы `.split()` и `.join()`, списковые включения" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Метод `.split()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Мы уже достаточно хорошо знакомы с вводом какой-то информации с клавиатуры с помощью `input()`. Однако раньше мы всегда вводили с клавиатуры только один объект: одно число или одно слово... А можно ли вводить сразу несколько слов или чисел? Конечно, можно! Посмотрим." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите имена героев мюзикла Notre Dame de Paris: Гренгуар Фролло Эсмеральда\n" ] } ], "source": [ "heroes = input(\"Введите имена героев мюзикла Notre Dame de Paris: \")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выше мы ввели довольно длинную строку: перечислили имена четырёх героев через пробел. Посмотрим на неё." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Гренгуар Фролло Эсмеральда'" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "heroes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А теперь разобьем эту строку по пробелу, чтобы получить четыре отдельных имени – воспользуемся методом `.split()`." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Гренгуар', 'Фролло', 'Эсмеральда']" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "heroes_list = heroes.split() # по умолчанию деление по пробелу\n", "heroes_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Получили самый обычный список. Можем обращаться к его элементам и даже изменять их при желании:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Фролло'" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "heroes_list[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Разбивать строку на части можно по любому символу, достаточно указать нужный символ в скобках в `.split()`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите имена любимых героев NDDP через запятую: Фролло,Гренгуар\n" ] } ], "source": [ "fheroes = input(\"Введите имена любимых героев NDDP через запятую: \")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Фролло,Гренгуар'" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fheroes" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Фролло', 'Гренгуар']" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fheroes.split(\",\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Попробуем проделать то же самое, но для чисел." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите два числа: 1999 2018\n" ] } ], "source": [ "nums = input(\"Введите два числа: \")\n", "nums_list = nums.split()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Список получили, но он пока состоит из строк, не из чисел." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1999', '2018']" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nums_list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Поправим: превратим все элементы списка в целые числа с помощью цикла и сохраним их в новый список." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1999, 2018]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "final = []\n", "\n", "for n in nums_list:\n", " final.append(int(n))\n", "final" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Чуть позже мы узнаем, как более удобно (и что приятно, более быстро) сделать то же самое, но без цикла.\n", "\n", "А пока узнаем, можно ли разбить строку по пустоте? То есть просто разделить её на отдельные символы. Попробуем." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "ename": "ValueError", "evalue": "empty separator", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m\"1234\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msplit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# нельзя\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mValueError\u001b[0m: empty separator" ] } ], "source": [ "\"1234\".split(\"\") # нельзя" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python сообщает нам, что разделитель пустой, и делить строку на части не хочет. Однако это возможно с помощью функции `list()`, при применении к строке она возвращает список символов в строке:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['1', '2', '3', '4']" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(\"1234\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Раз есть метод для разбиения строки на список строк, должна быть и обратная операция – для склеивания списка строк в одну большую строку. И такой метод действительно есть!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Метод `.join()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Пусть у нас есть список, состоящий из имени и фамилии. " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "l = ['Daniel', 'Lavoie']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "И мы хотим элементы списка склеить в одну строку так, чтобы между именем и фамилией был пробел. " ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Daniel Lavoie'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\" \".join(l) # вуаля" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Могли бы вместо пробела в кавычках поставить любой символ, например, нижнее подчеркивание." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Daniel_Lavoie'" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"_\".join(l)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Важно:** метод `.join()` берет в качестве аргумента именно список, не просто перечисленные элементы через запятую." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "join() takes exactly one argument (2 given)", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0mb\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'8'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 3\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0;34m\" \"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mjoin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0ma\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mb\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;31m# не работает\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: join() takes exactly one argument (2 given)" ] } ], "source": [ "a = '7'\n", "b = '8'\n", "\n", "\" \".join(a,b) # не работает" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'7 8'" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\" \".join([a, b]) # работает" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Иногда удобно сочетать методы `.split()` и `.join()`. Например, перед нами стоит такая задача. Пользователь вводит дату в формате день-месяц-год через точку, а мы хотим выводить на экран дату в том же формате, но через дефис. Чтобы решить эту задачу, можно взять введенную пользователем строку с датой, разбить её по точке, а затем склеить части, но уже используя дефис." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите дату: 20.04.2018\n" ] }, { "data": { "text/plain": [ "'20.04.2018'" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "date = input(\"Введите дату: \") \n", "date" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "20-04-2018\n" ] } ], "source": [ "parts = date.split(\".\")\n", "result = \"-\".join(parts)\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Метод `.strip()`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "При работе со строками можно столкнуться с еще одной сложностью – с пробелами. Если мы попросим пользователя ввести имена героев через запятую, перед запятой он, скорее всего, поставит пробел." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите имена любимых героев NDDP через запятую: Фролло, Гренгуар\n" ] }, { "data": { "text/plain": [ "'Фролло, Гренгуар'" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# вот так\n", "\n", "fheroes = input(\"Введите имена любимых героев NDDP через запятую: \")\n", "fheroes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как быть? Разбить строку по запятой, а потом избавиться от пробела. Избавиться от пробела поможет метод `.strip()`." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Гренгуар'" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "hero = \" Гренгуар\"\n", "hero.strip()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Метод `.strip()` работает по-умному: он убирает пробелы в начале и в конце строки, пробелы в середине (между словами) никуда не денутся." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Гренгуар Фролло'" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "heroes = \" Гренгуар Фролло \"\n", "heroes.strip()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "На самом деле это не случайно. Если посмотреть на все методы для строк, то можно увидеть целых три варианта со `.strip()`: \n", "\n", "* `.lstrip()`;\n", "* `.rstrip()`;\n", "* `.strip()`. \n", "\n", "Метод `.lstrip()` убирает все пробелы в начале строки (слева, *l* – от *left*), метод `.rstrip()` – все пробелы в конце строки (справа, *r* – от *right*), а метод `.strip()`является их объединением, то есть убирает все пробелы в начале и в конце строки." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Гренгуар '" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\" Гренгуар \".lstrip()" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "' Гренгуар'" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\" Гренгуар \".rstrip()" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Гренгуар'" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\" Гренгуар \".strip()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Списковые включения" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сейчас мы познакомимся со списковыми включениями (*list comprehensions*) – выражения, которые позволяют создавать новые списки на основе старых, без создания пустого списка и заполнения его в цикле. Чтобы понять, что такое списковые включения, лучше сначала посмотреть на пример. Попробуем создать список, состоящий из элементов другого списка, возведённых в квадрат." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 0, 9, 25]" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "old = [1, 0, 3, 5]\n", "new = [i**2 for i in old]\n", "new" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Цикл `for` у нас по-прежнему задействован, только немного в ином качестве и в другой последовательности: сначала мы указываем, что нужно делать с элементами, а потом – по каким элементам будем пробегаться. В нашем примере мы говорим, что возводим элементы `i` в квадрат, а потом сообщаем, что эти элементы `i` мы берём из списка `old`. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Вернемся к примеру с числами, введёнными с клавиатуры, и попробуем получить список из чисел, используя списковые включения." ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите числа через пробел: 5 6 9\n" ] }, { "data": { "text/plain": [ "[5, 6, 9]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "snumbers = input(\"Введите числа через пробел: \")\n", "lnumbers = snumbers.split()\n", "\n", "numbers = [int(n) for n in lnumbers] # делаем все элементы целочисленными\n", "numbers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Использование списковых включений – не только более компактный, но и более быстрый способ создания новых списков на основе старых. Давайте сравним время выполнения кода с циклом `for` и кода со списковыми включениями." ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.8 ms ± 61.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "%%timeit\n", "\n", "# timeit - магическое слово Jupyter, magic command,\n", "# которое позволяет измерить время исполнения ячейки с кодом\n", "# ячейка запускается много раз, и выводится среднее время выполнения со стандарным отклонением\n", "# должно быть указано первой строчкой в ячейке\n", "\n", "L = []\n", "for i in range(1, 10001):\n", " L.append(i**2)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2.39 ms ± 11.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ "%%timeit\n", "\n", "L = [j**2 for j in range(1, 10001)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "А теперь напишем компактный и быстрый код, который будет разбивать строку по запятой и при этом возвращать имена героев без лишних пробелов." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Введите имена героев через запятую: Квазимодо, Фролло, Феб\n" ] }, { "data": { "text/plain": [ "['Квазимодо', 'Фролло', 'Феб']" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "names = input(\"Введите имена героев через запятую: \")\n", "lnames = names.split(\",\")\n", "names_final = [name.strip() for name in lnames] # берем все элементы и отсекаем пробелы\n", "names_final # готово!" ] } ], "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.6.8" } }, "nbformat": 4, "nbformat_minor": 2 }