{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "

Algorytmy i programowanie

\n", "

Podstawowe struktury danych: listy

\n", "

\n", "
\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Listy\n", "\n", "Lista jest uporządkowaną oraz modyfikowalną sekwencją wartości, które mogą być dowolnego typu. Wartości te nazywamy elementami listy.\n", "\n", "Jedną z najprostszych metod utworzenia listy w Pythonie jest podanie jej elementów w nawiasach kwadratowych. Elementy te należy oddzielić przecinkami.\n", "\n", "Natomiast w celu wyświetlenia zawartości listy wystarczy zastosować funkcję `print()`, gdzie w miejsce argumentu tej funkcji wpisujemy nazwę listy.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", "['cyjan', 'magenta', 'żółty', 'czarny']\n" ] } ], "source": [ "cyfry = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n", "kolory_cmyk = [\"cyjan\", \"magenta\", \"żółty\", \"czarny\"]\n", "print(cyfry)\n", "print(kolory_cmyk)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Listę pustą deklarujemy poprzez `[ ]`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "pusta_lista = []" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Elementami listy mogą być wartości dowolnego typu, w tym inne listy lub wyrażenia. Co więcej, elementy jednej listy mogą być różnych typów. Zatem dopuszczalne są następujące struktury." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['słowo', True, 3.14, 5, 'aaaaa', [1, 2, 3]]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "W celu dostępu do elementów listy używa się operatora w postaci nawiasów kwadratowych. Zatem składnia dostępu do elementu stojącego na pozycji `k` jest następująca: `nazwa_listy[k]`.\n", "Wyrażenie wewnątrz nawiasów określa indeks. Indeksy w listach w Pythonie zaczynają się od 0. Zatem `nazwa_listy[0]` zwraca pierwszy element tej listy.\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "słowo\n", "True\n", "3.14\n", "5\n", "aaaaa\n", "[1, 2, 3]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "print(roznosci[0])\n", "print(roznosci[1])\n", "print(roznosci[2])\n", "print(roznosci[3])\n", "print(roznosci[4])\n", "print(roznosci[5])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Jeżeli nie znamy indeksu ostatniego elementu w liście, możemy posłużyć się funkcją `len()`, która zwraca długość listy, czyli liczbę jej elementów. Wówczas ostatni element listy `lista` znajduje się w polu o indeksie `len(lista)-1`.\n", "\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Ostatni element listy roznosci to: [1, 2, 3]\n" ] } ], "source": [ "print(\"Ostatni element listy roznosci to:\", roznosci[len(roznosci)-1])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Listy są zmienne tzn. możemy zmieniać elementy z listy." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['słowo', False, 3.14, 5, 'aaaaa', [1, 2, 3]]\n", "['słowo', False, 3.14, 5, 'aaaaa', 'brak listy']\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "roznosci[1] = False\n", "print(roznosci)\n", "\n", "roznosci[5] = \"brak listy\"\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "W szczególności aby zamienić miejscami dwa pola listy, np. pola o indeksach \\\\\\(i\\\\\\) i \\\\\\(j\\\\\\), możemy wprowadzić pomocniczą zmienną \\(zwyczajowo nazywa się ją `temp` od słowa _temporary_\\), w której przechowujemy wartość pola listy, które zostaje nadpisane nową wartością znajdującą się w innym polu. To znaczy w pomocnicznej zmiennej zapisujemy wartość pola znajdującego się pod indeksem \\\\\\(i\\\\\\), następnie w to pole wpisujemy wartość pola znajdującego się pod indeksem \\\\\\(j\\\\\\). Gdybyśmy tego nie zrobili, po nadpisaniu stracilibyśmy informację o tym, co znajdowało się w polu o indeksie \\\\\\(i\\\\\\) i nie moglibyśmy wpisać tej wartości w pole o indeksie \\\\\\(j\\\\\\).\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['słowo', False, 3.14, 5, 'aaaaa', 'brak listy']\n", "[False, 'słowo', 3.14, 5, 'aaaaa', 'brak listy']\n" ] } ], "source": [ "print(roznosci)\n", "temp = roznosci[0] # aby zamienić miejscami dwa pola listy wprowadzamy pomocniczą zmienną temp\n", "roznosci[0] = roznosci[1]\n", "roznosci[1] = temp\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Uwaga:** Każde wyrażenie będące liczbą całkowitą może być użyte jako indeks w liście. Należy jednak pamiętać, że podanie zbyt dużej liczby może wywołać błąd, \n", "jeśli lista nie będzie zawierała elementu o takim indeksie. Natomiast podanie liczby ujemnej jako indeks spowoduje obliczanie indeksu od końca listy.\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n", "słowo\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1,2,3]]\n", "print(roznosci[-1])\n", "print(roznosci[-6])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Podstawowe operacje na listach\n", "\n", "Do list możemy stosować operator dodawania i mnożenia, które odpowiednio oznaczają konkatenację \\(czyli sklejenie\\) dwóch list oraz powtórzenie listy zadaną liczbę razy.\n" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3, 4, 5, 6]\n", "[0, 1, 0, 1, 0, 1]\n" ] } ], "source": [ "lista1 = [1, 2, 3] + [4, 5, 6]\n", "print(lista1)\n", "\n", "lista2 = [0, 1]*3\n", "print(lista2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Możemy również pobrać wycinek listy i wyświetlić go lub przypisać w miejsce nowej zmiennej. W celu uzyskania wycinka listy należy użyć składni: `moja_lista[n:m]`, \n", "której wywołanie zwróci elementy listy `moja_lista` od pozycji o indeksie `n`, do pozycji o indeksie `m-1`. \n", "\n", "Co więcej, jeśli indeks przed dwukropiem nie zostanie podany, to automatycznie wycięcie nastąpi od pierwszego elementu w liście. Brak indeksu po dwukropku spowoduje zakończenie wycinania na ostatnim elemencie z listy. \n", "\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[True, 3.14, 5]\n", "['słowo', True, 3.14, 5]\n", "[3.14, 5, 'aaaaa']\n", "[3.14, 5, 'aaaaa', [1, 2, 3]]\n", "['słowo', True, 3.14, 5, 'aaaaa', [1, 2, 3]]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "print(roznosci[1:4])\n", "print(roznosci[:4])\n", "print(roznosci[2:-1])\n", "print(roznosci[2:])\n", "print(roznosci[:])" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[True, False, 'Pi', [1, 2, 3], [4, 5]]\n" ] } ], "source": [ "roznosci[1:3] = [False, \"Pi\"]\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "W celu dodania lub usunięcia elementu z list w Pythonie zostały zostały stworzone odpowiednie **metody**. W Pythonie wszystkie zmienne są w rzeczywistości **obiektami**. \n", "Na przykład zmienna typu `int` jest w rzeczywistości obiektem klasy `int`. **Klasa** definiuje typ danych \\(i jego metody\\), natomiast obiekt jest instancją \\(przypadkiem\\) klasy.\n", "Najważniejsze co musimy na tym etapie wiedzieć o obiektach to to, że każdy obiekt składa się z danych oraz metod. \n", "\n", "Tak jest też w przypadku list. Danymi w tym wypadku jest sekwencja wartości. Każda lista jako obiekt ma również wiele metod, które wymienione są np. tutaj https://docs.python.org/3/tutorial/datastructures.html. \n", "Najważniejszymi na ten moment są metody: \n", "\n", "- `append()`, która dodaje element podany jako argument na koniec listy,\n", "- `pop()`, która usuwa z listy element stojący na pozycji podanej jako argument tej metody, zwraca ona usunięty element,\n", "- `remove()`, która usuwa element z listy, który został podany jako argument metody \\(argumentem musi być wartość elementu, a nie jego indeks\\).\n", "\n", "W celu wywołania danej metody używamy składni `nazwa_listy.nazwa_metody(argument)`.\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['słowo', True, 3.14, 5, 'aaaaa', [1, 2, 3], [4, 5]]\n", "[True, 3.14, 5, 'aaaaa', [1, 2, 3], [4, 5]]\n", "[True, 3.14, 'aaaaa', [1, 2, 3], [4, 5]]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "\n", "roznosci.append([4, 5])\n", "print(roznosci)\n", "roznosci.pop(0) # można użyc print(roznosci.pop(0)) lub nowa_zmienna = roznosci.pop(0)\n", "print(roznosci)\n", "roznosci.remove(5)\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Istnieje również instrukcja `del`, która nie jest już metodą, ale jej działania jest podobne do `pop()`. Nie zwraca ona jednak żadnej konkretnej wartości (dokładniej zwraca `None`), ale za to pozwala usuwać całe wycinki listy." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[True, 3.14, 5, 'aaaaa', [1, 2, 3]]\n", "[True, 'aaaaa', [1, 2, 3]]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "\n", "del(roznosci[0]) nie wystarczy podać samego indeksu, bo to nie jest metoda\n", "print(roznosci)\n", "del(roznosci[1:3])\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Poruszanie się po listach\n", "\n", "Jednym ze sposobów na sprawdzenie po kolei wszystkich elementów danej listy jest użycie pętli `for`. Może nam się to przydać np. w momencie gdy chcemy wypisać po kolei elementy danej listy, każdy w kolejnej linii." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "słowo\n", "True\n", "3.14\n", "5\n", "aaaaa\n", "[1, 2, 3]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "for element in roznosci :\n", " print(element)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.14\n", "5\n", "aaaaa\n", "[1, 2, 3]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "for element in roznosci[2:] :\n", " print(element)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Powyższe rozwiązanie sprawdza się w sytuacji, gdy musimy tylko odczytać elementy z listy. Natomiast jeśli chcemy ją zmodyfikować, to potrzebujemy przeglądania listy po indeksach. W takiej sytuacji potrzebna jest znajomość rozmiaru listy, którą uzyskuje się za pomocą przytoczonej już funckji `len()`. Przypomnijmy, że zwraca ona jako wartość liczbę elementów w liscie.\n" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "6\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "print(len(roznosci))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Kiedy znamy już rozmiar listy, to możemy do jej przejrzenia użyć instrukcji `while` w następujący sposób." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "słowo\n", "True\n", "3.14\n", "5\n", "aaaaa\n", "[1, 2, 3]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "i = 0\n", "while i < len(roznosci) :\n", " print(roznosci[i])\n", " i = i + 1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Możemy wówczas dowolnie modyfikować listę, np. wpisując jakąś ustaloną wartość na pozycjach o indeksie nieparzystym." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['słowo', None, 3.14, None, 'aaaaa', None]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "i = 0\n", "while i < len(roznosci) :\n", " if i % 2 == 1 :\n", " roznosci[i] = None\n", " i = i + 1\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Jednak w sytuacjach, gdy kolejne iteracje pętli wykonywane są dla pewnej zmiennej, która w kolejnych iteracjach zmienia się w ten sam sposób (w powyższym przypadku zwiększa się o 1), \n", "stosuje się znacznie częściej instrukcję `for` w połączeniu z funkcją `range()`. Funkcję `range()` możemy wywołać w następujących konfiguracjach:\n", " - `range(stop)` zwraca ciąg liczb *0, 1, 2,... , stop-1*,\n", " - `range(start, stop)` zwraca ciąg liczb *start, start+1, start+2, ..., stop-1*,\n", " - `range(start, stop, step)` zwraca ciąg arytmetyczny liczb nieprzekraczających *stop* o wyrazie początkowym *start* i różnicy *step*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wypisywanie po kolei wszystkich elementów listy możemy zatem przeprowadzić w jeszcze inny sposób, tzn. tak jak poniżej." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "słowo\n", "True\n", "3.14\n", "5\n", "aaaaa\n", "[1, 2, 3]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "for i in range(len(roznosci)) :\n", " print(roznosci[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Możemy również łatwo zmodyfikować tylko elementy o nieparzystych indeksach bez użycia dodatkowej instrukcji `if`." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['słowo', None, 3.14, None, 'aaaaa', None]\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "for i in range(1, len(roznosci), 2) :\n", " roznosci[i] = None\n", "print(roznosci)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "W miejsce trzeciego argumentu funkcji `range()` możemy również wstawiać liczby ujemne, co pozwala np. łatwo wyświetlić listę w odwrotnej kolejności." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 2, 3]\n", "aaaaa\n", "5\n", "3.14\n", "True\n", "słowo\n" ] } ], "source": [ "roznosci = [\"słowo\", True, 3.14, 2+3, \"a\"*5, [1, 2, 3]]\n", "for i in range(len(roznosci)-1, -1, -1) :\n", " print(roznosci[i])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Listy zagnieżdżone\n", "\n", "Zauważmy, że powyższe metody traktują listy będące elementami innej listy jako pojedyncze elementy. Co jednak jeśli mielibyśmy listę, której elementami są inne listy, i chcielibyśmy zmodyfikować elementy tych zagnieżdżonych list? Wówczas rozwiązaniem jest użycie pętli wewnątrz pętli." ] }, { "cell_type": "code", "execution_count": 127, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 2 3 4 \n", "0 0 0 \n", "-1 3 \n" ] } ], "source": [ "A = [[1, 2, 3, 4], [0, 0, 0], [-1, 3]]\n", "for i in range(len(A)) :\n", " for j in range(len(A[i])) : # A[i] jest listą więc len() zwraca jej długość\n", " print(A[i][j], end=\" \") # A[i][j] zwraca element lężący na j-tej pozycji na liście A[i], czyli liście będącej i-tym elementem listy A\n", " print(\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Uwaga:** Funkcja `print()` domyślnie kończy wypisywanie swoich argumentów za pomocą przejścia do nowej linii. Jeśli chcemy to zmienić możemy użyć dodatkowego argumentu `end=\"znak_koncowy\"`\n", "który spowoduje, że podany `znak_koncowy` pojawi się po wypisaniu każdego elementu podanego w argumencie funkcji `print()`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Jak wczytywać listy?\n", "\n", "W wielu przypadkach będziemy chcieli stworzyć listę na podstawie danych podanych przez użytkownika lub wczytanych z pliku. Przyda nam się do tego metoda `split()`, która dzieli dany obiekt typu `string` tworząc z niego listę. Domyślnie elementy listy są od siebie odseparowane spacją." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['To', 'jest', 'przykładowy', 'tekst']\n" ] } ], "source": [ "tekst = \"To jest przykładowy tekst\"\n", "nowa_lista = tekst.split()\n", "print(nowa_lista)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Funkcja `split()` umożliwia też dzielenie tekstu względem wybranego przez nas separtora, czyli znaku lub ciągu znaków, które mają oddzielać od siebie kolejne elementy listy. Wówczas podajemy ten seprator jako argument funkcji `split()`. " ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']\n" ] } ], "source": [ "cyfry = \"0, 1, 2, 3, 4, 5, 6, 7, 8, 9\"\n", "cyfry_lista = cyfry.split(\", \")\n", "print(cyfry_lista)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "W powyższym przykładzie wczytany tekst zostaje zamieniony na listę elementów typu `string`. To oznacza, że każdy z elementów tak utworzonej listy jest tekstem. Gdybyśmy jednak chcieli uzyskać listę obiektów innego typu, np. listę liczb całkowitych, wówczas możemy zastosować poznaną już funkcję `int()`, która rzutuje obiekt typu `string` na obiekt typu `int`." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] } ], "source": [ "cyfry = \"0, 1, 2, 3, 4, 5, 6, 7, 8, 9\"\n", "cyfry_lista = cyfry.split(\", \")\n", "for i in range(len(cyfry_lista)):\n", " cyfry_lista[i] = int(cyfry_lista[i])\n", "print(cyfry_lista)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Nie jest to specjalnie elegancki sposób wczytania i przekonwertowania danych. Możemy zrobić to prościej. Aby wykonać jednoczesne rzutowanie wszystkich elementów listy posłużymy się funkcją `map()`, która przyjmuje dwa argumenty: funkcję `funkcja` i obiekt iterowalny `kolekcja_danych` (np. listę), a następnie stosuje funkcję `funkcja` do wszystkich elementów obiektu `kolekcja_danych`. Musimy jeszcze zastosować do wyniku tej operacji funkcję `list()`, która wyprodukuje nam końcową listę." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n" ] } ], "source": [ "cyfry = \"0, 1, 2, 3, 4, 5, 6, 7, 8, 9\"\n", "cyfry_lista = list(map(int, cyfry.split(\", \")))\n", "print(cyfry_lista)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] }, { "cell_type": "markdown", "metadata": {}, "source": [] } ], "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.9" } }, "nbformat": 4, "nbformat_minor": 4 }