{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Python\n", "\n", "## Типы данных-коллекции\n", "\n", "### Раздел 1. Типы данных-последовательности\n", "\n", "Тип данных, реализующий последовательность, это такой тип данных, для которого доступен оператор вхождения, функция получения длины (len()), получения среза и к нему можно доступаться как к итератору. Мы уже рассмотрели тип str, в будущем будут рассмотрены bytearray и bites, а на данный момент сосредоточимся на tuple и лист. Все эти типы являются встроенными.\n", "\n", "#### Кортежи\n", "\n", "Кортежем (tuple) называется упорядоченная последовательность из 0 или более ссылок на объекты. Кортежи поддерживают тот же оператор извлечения среза, что и строки. Кортежи являются неизменяемыми, так что если в ходе работы вам необходима изменяемая последовательность — обратитесь к списком, либо преобразуйте кортеж в список при помощи вызова list().\n", "\n", "Как и всегда, tuple может быть вызван как функция. Без передачи аргумента, будет возвращен пустой объект. При передачи в качестве аргумента объекта типа tuple, будет создана его поверхностная копия. Во всех остальных случаях будет совершена попытка привести аргумент к типу tuple. Данная функция принимает не более одного аргумента.\n", "\n", "Кортежи могут быть созданы заключением элементов в скобки (даже если элементов нет () — пустой кортеж) или перечислением элементов кортежа через запятую (только не при передачи таким образом кортежа в качестве аргумента функции.\n", "\n", "Для кортежей доступны всего два метода: count и index. t.count(x) возвращает число вхождений x в t, а t.index(x) возвращает индекс самого левого вхождения x в t, либо возбуждает ValueError, если x не входит в t. К тому же, для кортежей доступны операторы + (конкатенация), \\* (повторение), [] (взятие среза), in и not in (проверка на вхождение). Комбинированные версии присваивания += и \\*= могут использоваться, несмотря на то, что кортежи неизменяемы. Просто будет создан новый объект, который будет присвоен переменной (то же происходит и со строками). Для кортежей доступны операции сравнения: < <= == != >= >. Они выполняют сравнение поэлементно и рекурсивно.\n", "\n", "Кортежи позволяют выполнить один из самых известных трюков в Python:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2 1\n" ] } ], "source": [ "x = 1\n", "y = 2\n", "\n", "x, y = y, x\n", " \n", "print(x, y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Также, если элементами итератора являются кортежи, то вы их сразу можете распаковывать:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Batman vs Joker\n", "Superman vs Lex Luthor\n", "Green Lantern vs Sinestro\n" ] } ], "source": [ "for hero, villain in (('Batman', 'Joker'), ('Superman', 'Lex Luthor'), ('Green Lantern', 'Sinestro')):\n", " print(hero, 'vs', villain)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Списки\n", "\n", "Список есть упорядоченная последовательность из нуля или более ссылок на объекты. Как и кортежи, для списков доступно взятие среза. Но в отличие от кортежей, списки изменяемы. Как следствие, мы можем изменять не только список целиком или его отдельные элементы, но и целые срезы в нем.\n", "\n", "Тип list может быть вызван как функция. Без аргументов list() вернет пустой список. Если в качестве аргумента был передан объект типа list, то будет создана его поверхностная копия. Если аргумент дан, но не является объектом класса list, то будет произведена попытка приведения его к типу list. Данная функция принимает не более одного аргумента.\n", "\n", "Списки могут быть созданы также перечислением элементов через запятую в квадратных скобках или при помощи генераторов списков.\n", "\n", "Для типа list доступны операции сравнения, работают аналогично случаю с кортежами.\n", "\n", "Списки могут быть вложены в друг друга, проитерированы, от них можно взять срез, — все то же, что и для кортежей. Можно использовать операторы in, not int, +, +=, *, *=, функцию len() и конструкцию del.\n", "\n", "Также есть оператор \\*, который может использоваться для получения остальной части списка в случае выбора нескольких элементов:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Batman ['Superman', 'Green Lantern', 'Flash']\n", "Batman ['Superman', 'Green Lantern'] Flash\n" ] } ], "source": [ "heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash']\n", "\n", "batman, *rest = heroes\n", "print(batman, rest)\n", "\n", "batman, *mid, flash = heroes\n", "print(batman, mid, flash)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Также есть оператор распаковки \\*, который используется для передачи элементов списка по отдельности в качестве аргументов функции:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Batman\n", "Superman\n", "Green Lantern\n", "Flash\n" ] } ], "source": [ "heroes = ['Batman', 'Superman', 'Green Lantern', 'Flash']\n", "\n", "def print4(a, b, c, d):\n", " print(a)\n", " print(b)\n", " print(c)\n", " print(d)\n", " \n", "print4(*heroes)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для списков доступны следующие методы:\n", "\n", "| Метод | Описание |\n", "| ----- | :------- |\n", "| L.append(x) | Добавляет элемент x в конец списка L |\n", "| L.count(x) | Подсчитывает число вхождений элемента x в список L |\n", "| L.extend(m) | Добавляет элементы m, к которому должен быть обеспечен доступ как к итератору, в конец списка L (то же, что и L += m |\n", "| L.index(x, start, end) | Возвращает индекс самого левого вхождения x в L. Если start и end заданы, то поиск происходит в срезе L[start:end]. Если элемент не найден, то возбуждается исключение ValueError |\n", "| L.insert(i, x) | Вставляет элемент x на позицию с индексом i в список L |\n", "| L.pop() | Удаляет последний элемент списка L и возвращает его |\n", "| L.pop(i) | Удаляет элемент списка L с индексом i и возвращает его |\n", "| L.remove(x) | Удаляет самое левое вхождение элемента x из списка L. Если элемент не найден, то возбуждается исключение ValueError |\n", "| L.reverse() | Разворачивает список |\n", "| L.sort(...) | Сортирует список. Аргумент key отвечает за функцию порядка, а reversed за способ сортировки (по невозрастанию / неубыванию) |\n", "\n", "#### Генераторы списков\n", "\n", "Для получения списка как результат какой-то операции над итератором используются генераторы списков. Есть две формымы генератора списков:\n", "\n", "[выражение for элемент in итератор]\n", "\n", "[выражение for элемент in итератор if условие]\n", "\n", "В самом простом случае выражение и есть элемент:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", "[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]\n", "[0, 6, 12, 18]\n" ] } ], "source": [ "print([x for x in range(10)])\n", "print([2*x for x in range(10)])\n", "print([2*x for x in range(10) if x % 3 == 0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Последняя форма в разы удобнее, чем запись:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 6, 12, 18]\n" ] } ], "source": [ "lst = []\n", "for x in range(10):\n", " if x % 3 == 0:\n", " lst.append(2*x)\n", "print(lst)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Раздел 2. Типы-множества\n", "\n", "Типы данных-множества — типы, для которых доступны оператор вхождения in, функция len и доступ, как к итератору. В Python есть два типа множеств — set и frozenset. Тип frozenset является неизменяемым, поэтому для него недоступны методы для типа set, которые изменяют объект. В остальном они идентичны.\n", "\n", "Только хешируемые объекты могут быть добавлены в множество. Все встроенные неизменяемые типы являются хешируемыми.\n", "\n", "#### Множества\n", "\n", "Множество типа set есть неупорядоченная коллекция из нуля или более ссылок на хешируемые объекты. Множества изменяемы, так что в них можно добавлять и удалять элементы. Будучи неупорядоченными, понятие индекса или позиции для них не имеет смысла.\n", "\n", "Для создания множества используется либо вызов set(), который без аргументов создает пустое множество. С аргументом типа set создается поверхностная копия множества, а с другим аргументом выполняется попытка приведения к множеству. Данная функция не принимает более одного аргумента. Множество может быть также создано путем заключения элементов в фигурные скобки, но стоит учитывать, что пустое множество не может быть создано таким образом, так как {} создаст пустой словарь.\n", "\n", "Множество всегда хранит уникальные элементы:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "set('hello world') == {'h', 'e', 'l', 'o', ' ', 'w', 'r', 'd'}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Множества поддерживают функцию len(), которое возвращает число элементов множества.\n", "\n", "Для типа set доступны следующие методы:\n", "\n", "| Метод | Описание |\n", "| ----- | :------- |\n", "| s.add(x) | Добавляет элемент x во множество s, если такого там нет |\n", "| s.clear() | Удаляет все элементы множества s |\n", "| s.copy() | Создает неглубокую копию множества s |\n", "| s.difference(t) | Возвращает новое множество, которое содержит все элементы множества s, которые не входят во множество t. Эквивалент s - t |\n", "| s.differenct_update(t) | Удаляет все элементы t из множества s. Эквивалент s -= t |\n", "| s.discard(x) | Удаляет элемент x из множества s |\n", "| s.intersection(t) | Возвращает новое множество, которое содержит элементы множества s, которые в то же время являются элементами множества t. Эквивалент s & t |\n", "| s.intersection_update(t) | Удаляет из множества s все элементы, которые не входят в t. Эквивалент s &= t |\n", "| s.isdisjoint(t) | Возвращает True, если у множеств s и t нет общих элементов |\n", "| s.issubset(t) | Возвращает True, если s есть подмножество (несобственное) t. Эквивалент s <= t |\n", "| s.issuperset(t) | Возвращает True, если t есть подмножество (несобственное) s. Эквивалент t <= s |\n", "| s.pop() | Удаляет случайный элемент s и возвращает его. Возбуждает исключение KeyError, если множесво пустое |\n", "| s.remove(x) | Удаляет элемент x из множества s. Возбуждает исключение KeyError, если x не входит в s |\n", "| s.symmetric_difference(t) | Возвращает новое множество, которое содержит элементы, которые либо содержатся в s и не содержатся в t, либо содержатся в t и не содержатся в s. Эквивалент s ^ t |\n", "| s.symmetric_difference_update(t) | Записывает в s симметрическую разность s и t. Эквивалент s ^= t |\n", "| s.union(t) | Возвращает новое множество, которое содержит все элементы множеств s и t |\n", "| s.update(t) | Добавляет все элементы множества t во множество s |\n", "\n", "Последние два метода эквивалентны s | t и s |= t соответственно.\n", "\n", "#### Генераторы множеств\n", "\n", "В целом то же, что и для списков, но сам генератор обозначается фигурными скобками:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}\n", "{0, 2, 4, 6, 8, 10, 12, 14, 16, 18}\n", "{0, 18, 12, 6}\n" ] } ], "source": [ "print({x for x in range(10)})\n", "print({2*x for x in range(10)})\n", "print({2*x for x in range(10) if x % 3 == 0})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Раздел 3. Типы-отображения\n", "\n", "Типы отображения — наборы пар \"ключ - значение\". Единственным встроенным типом-отображением является dictionary, который отображает хешируемые объекты на ссылки на другие объекты.\n", "\n", "#### Словари\n", "\n", "Тип dict (словарь) это упорядоченная коллекция пар \"ключ - значение\". Раньше dict был неупорядоченным типом, поэтому во избежания ошибок при исполнении кода более старыми интерпретаторами, советую вам придерживаться отношения к словорям как к неупорядоченным последовательностям.\n", "\n", "Словари поддерживают оператор вхождения in, функцию len(), и доступ как к итератору. Среди операций сравнения поддерживаются только == и !=.\n", "\n", "Как и обычно, тип dict может быть вызван как функция. Если ей не переданы аргументы, то создается пустой словарь. Если передан аргумент с типом-отображением, то создается словарь на основе этого аргумента. Например, если аргумент типа dict, то будет создана его поверхностная копия. Можно также передать аргумент-последовательность, каждый элемент в котором — последовательность из двух элементов: ключа и значения. Также пустой словарь можно создать при помощи конструкции {}, а словарь с элементами — перечислив пары ключ - значение в фигурных скобках через запятую, отделяя ключи от значений двоеточием:\n", "\n", "{ключ1 : значение1, ключ2 : значение2}\n", "\n", "К тому же доступны генераторы словарей.\n", "\n", "Мы можем доступаться к элементам словаря, используя ключ: d[ключ] вернет значение элемента с ключем *ключ*.\n", "\n", "Для добавления новых элементов используется оператор присваивания: d[новый\\_ключ] = новое\\_значение.\n", "\n", "Для типа dict доступны следующие методы:\n", "\n", "| Метод | Описание |\n", "| ----- | :------- |\n", "| d.clear() | Удаляет все элементы словаря d |\n", "| d.copy() | Возвращает поверхностную копию словаря d |\n", "| d.fromkeys(s, v) | Возвращает словарь, где ключами являются элементы последователности s, а значения равны v или None, если v не указано |\n", "| d.get(k) | Возвращает значение, которое соответствует ключу k или None, если такого нет |\n", "| d.get(k, v) | Возвращает значение, которое соответствует ключу k или v, если такого нет |\n", "| d.items() | Возвращает view (read-only итератор), содержащий пары (ключ, значение) словаря d |\n", "| d.keys() | Возвращает view, содержащий ключи словаря d |\n", "| d.pop(k) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возбуждается исключение KeyError |\n", "| d.pop(k, v) | Удаляет элемент, соответствующий ключу k и возвращает его. Если такого нет, то возвращается значение v |\n", "| d.popitem() | Удаляет из словаря d и возвращает некоторую пару (ключ, значение). Возбуждает исключение KeyError, если d пуст |\n", "| d.setdefault(k, v) | ТТо же, что и d.get(k), но если такого элемента нет, то создается новый со значением None или v, если указано |\n", "| d.values() | Возвращает view всех значений словаря d |\n", "\n", "#### Генераторы словарей\n", "\n", "Генераторы словарей имеют две уже знакомые нам формы:\n", "\n", "[выражение\\_ключа : выражение\\_значения for ключ, значение in итератор]\n", "\n", "[выражение\\_ключа : выражение\\_значения for ключ, значение in итератор if условие]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Раздел 4. Итераторы. Функции и операци для работы с итераторами\n", "\n", "Тип данных называется итерируемым, если он может возвращать свои элементы по одному. Технически, любой объект, реализующий метод \\_\\_iter\\_\\_() является итерируемым и может предоставить итератор. Итератор — объект, реализуюзий метод \\_\\_next()\\_\\_, возвращающий следующее значение и возбуждающий исключение StopIteration, если элементов больше нет.\n", "\n", "Для итерируемых объектов доступны следующие функции и операторы:\n", "\n", "| Функция или оператор | Описание |\n", "| -------------------- | :------- |\n", "| s + t | Возвращает конкатенацию последовательностей s и t |\n", "| s * n | Возвращает последовательность, которая является результатом повторения последовательности s n раз |\n", "| x in t | Проверяет, есть ли в последовательности i элемент x |\n", "| all(i) | Возвращает True, если все элементы i при приведении к типу bool имеют значение True |\n", "| any(i) | Возвращает True, если один из элементов i при приведении к типу bool имеет значение True |\n", "| enumerate(i, start) | Возвращает последовательность элементов (индекс, значение) для итератора i, начиная с индекса 0 или start, если данный аргумент задан |\n", "| len(x) | Возвращает число элементов в x |\n", "| max(i, key) | Возвращает максимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key максимален |\n", "| min(i, key) | Возвращает минимальный элемент в i. Если аргумент key задан, то возвращает элемент, для которого key минимален |\n", "| range(start, stop, step) | Возвращает итератор, содержащий целые числа от start до stop с шагом step. Аргумент start включен в последовательность, а stop — нет |\n", "| reversed(i) | Возвращает итератор, в котором элементы идут в обратном порядке |\n", "| sorted(i, key, reverse) | Возвращает отсортированный по неубыванию итератор. Если задан аргумент key, то сортировка идет по значением key(x) для каждого элемента x. Если reversre указан как True, то сортировка идет по невозрастанию |\n", "| sum(i, start) | Возвращает сумму элементов в итераторе i плюс start. Если start не задан, то принимается равным 0 |\n", "| zip(i1, ..., iN) | Возвращает итератор из кортежей, состоящий из элементов i1, ..., iN. Количество кортежей определяется минимальной длиной i1, ..., iN |" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Раздел 5. Копирование коллекций\n", "\n", "При присваивании объектов будет выполнено присваивание по ссылке:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Alfred', 'Robin', 'Catwoman']\n" ] } ], "source": [ "x = ['Nightwing', 'Robin', 'Catwoman']\n", "y = x\n", "x[0] = 'Alfred'\n", "print(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для создания поверхностной копии можно использовать оператор среза:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Nightwing', 'Robin', 'Catwoman']\n" ] } ], "source": [ "x = ['Nightwing', 'Robin', 'Catwoman']\n", "y = x[:]\n", "x[0] = 'Alfred'\n", "print(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Но поверхностная копия имеет ограничения, связанные с тем, что при ее создании создаются копии лишь самих элементов. Если они в свою очередь являются ссылками, то мы снова столкнемся с проблемой:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[['Lex Luther', 'General Zod'], ['Joker', 'Pinguin']]\n" ] } ], "source": [ "dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]\n", "villains = dc_villains[:]\n", "\n", "dc_villains[1][1] = 'Pinguin'\n", "print(villains)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Чтобы избежать этого, необходимо использовать функцию deepcopy() модуля copy, которая создает глубокую копию объекта:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]\n" ] } ], "source": [ "from copy import deepcopy\n", "\n", "dc_villains = [['Lex Luther', 'General Zod'], ['Joker', 'Scarecrow']]\n", "villains = deepcopy(dc_villains)\n", "\n", "dc_villains[1][1] = 'Pinguin'\n", "print(villains)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Раздел 6. Практика: vedis и matplotlib\n", "\n", "Есть достаточно интересный модуль vedis, который предоставляет доступ к key-value store типа Redis на диске, но при этом API этого доступа совпадает с доступом к словарю.\n", "\n", "Решим с использованием модуля vedis [задачу о гипотезе Колатца](https://lambda-it.ru/post/26) и сделаем визуализацию при помощи matplotlib.\n", "\n", "Для начала напишем функцию, которая из текущего числа получает следующее в последовательности:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "def next_kolatz(x):\n", " if x & 1:\n", " return 3*x + 1\n", " return x >> 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если число x является нечетным (последний разряд в двоичной системе равен единице), то мы возвращаем 3x + 1. В противном случае, мы делим число на 2 (соответствует сдвигу вправо на один разряд в двоичной системе счисления). Теперь импортируем модуль vedis. При помощи него мы будем хранить длину цепочки для чисел, которые мы уже встречали. Также напишем функцию, которая вычисляет и возвращает длину цепочки для заданного числа x, записывает ее в хранилище, а также записывает в хранилище длины цепочек для всех промежуточных чисел:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "def get_kolatz_length(x, storage):\n", " cur = x\n", " try:\n", " return int(storage.get(cur))\n", " except KeyError as e:\n", " pass\n", " stack = [cur]\n", " while True:\n", " cur = next_kolatz(cur)\n", " try:\n", " length = int(storage.get(cur))\n", " break\n", " except KeyError as e:\n", " pass\n", " stack.append(cur)\n", " for item in stack[::-1]:\n", " length += 1\n", " storage.set(item, length)\n", " return int(storage.get(x))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь напишем функцию, которая возвращает число, меньшее заданного, для которого данная цепочка максимальна и длину цепочки:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "def get_max_kolatz_length(n, storage):\n", " num = max(range(1, n+1), key=lambda x: get_kolatz_length(x, storage))\n", " return num, get_kolatz_length(num, storage)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь остается только инициализировать хранилище и испытать наши функции:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(77031, 351)\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from vedis import Vedis\n", "\n", "db = Vedis('storage.db')\n", "db.set(1, 1)\n", "\n", "print(get_max_kolatz_length(100000, db))\n", "db.close()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь построим обычный scatter plot при помощи matplotlib:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "<Figure size 432x288 with 1 Axes>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", " \n", "db = Vedis('storage.db')\n", "\n", "X = range(1, 101)\n", "Y = [get_kolatz_length(x, db) for x in X]\n", " \n", "db.close()\n", "\n", "plt.scatter(X, Y)\n", "plt.title('Визуализация длин цепочек Колатца')\n", "plt.xlabel('Число')\n", "plt.ylabel('Длина цепочки')\n", "plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Раздел 7. Домашнее задание\n", "\n", "Решить задачи после третьей главы Саммерфилда\n", "\n", "Загуглить гипотезу Гольдбаха\n", "\n", "Загуглить динамическое программирование\n", "\n", "Написать функцию, которая принимает в качестве аргумента множество и функцию и при помощи matplotlib изображает график заданной функции на множестве значений. Добавьте несколько дополнительных аргументов: например, возможность сделать одну из осей логарифмической, выбрать разные цветовые схемы, разные типы графиков и т.д.\n", "\n", "Решить задачу, которая будет опубликована в субботу" ] } ], "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.6" } }, "nbformat": 4, "nbformat_minor": 2 }