{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Getting Started with Python \n", "\n", "### © Marko Petrović, University of Delaware\n", "[PHYS824: Nanophysics & Nanotechnology](https://wiki.physics.udel.edu/phys824) \n", "\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## What is Python?\n", "\n", "\n", "Python is an interpreted high-level programming language for general-purpose programming.\n", "\n", "#### Python features:\n", " - dynamic types \n", " - object-oriented, functional and procedural \n", " - large library (a lot of external packages)\n", " \n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### Different versions of Python\n", "\n", "- Python 2.x or 3.x \n", "\n", "#### Writing and running Python\n", "\n", "- Terminal (text scripts or interactively).\n", "- Notebooks ([Jupyter](http://jupyter.org/index.html)). To run this notebook you also need to install Python's [matplotlib](https://matplotlib.org/) and [numpy](http://www.numpy.org/) packages. \n", "- IDE (e.g. [Spyder](https://www.spyder-ide.org/), [PyCharm](https://www.spyder-ide.org/))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## What is covered in this notebook\n", "- Basic data types in Python\n", "- Data containers (lists, tuples, dictionaries)\n", "- Control flow (if statements, loops, functions)\n", "- Built-in functions\n", "- Pitfalls and error messages" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Basic data types in Python" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Most commonly used data types in Python are: integer, float, string, and boolean. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "hbar = 6.582119514e-34 # Reduced Plank constant (eV*s)\n", "n_spins = 5 # Integer\n", "phi = 0.1234 # Float \n", "z = 1 + 2j # Complex\n", "xlabel = 'System Size (nm)' # String \n", "test_failed = True # Boolean (True or False)\n", "is_phi_positive = phi > 0 # Boolean\n", "xyz = None # (None is not equal to zero)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "print(z)\n", "print(\"RE: %5.2f IM: %5.2e \" % (z.real, z.imag))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Python is dynamically typed language\n", "- Variable type doesn't depend on its name but on its current value. \n", "- Variable type can change with every new value assignment. \n", "- There are no constants in Python " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Example of a variable changing type\n", "\n", "You can check the type of a variable with the built-in type() function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "x = 5 \n", "print(x, type(x))\n", "\n", "x = \"Hello!\" \n", "print(x, type(x))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Convention for constants\n", "\n", "Use all capital letters as a reminder to keep a value constant. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "hbar = 6.582119514e-34 # We want this to be constant \n", "hbar = 2 # Problem: We just changed it!\n", "print('hbar = ', hbar)\n", "NEW_HBAR = 6.582119514e-34 # Constant (still can be changed!)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Operators" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "x = 12\n", "y = 2.7\n", "print('Addition: ', x + y)\n", "print('Subtraction: ', x - y)\n", "print('Multiplication: ', x * y)\n", "print('Power: ', x**y)\n", "print('Division: ', x / y)\n", "print('Floor division: ', x // y)\n", "print('Remainder: ', x % y)\n", "print('String concatenation', 'ABCD' + 'EFGH')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Logical operators" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "x = 2\n", "y = 7\n", "print('Equality: ', x == y)\n", "print('Non-equality: ', x != y)\n", "print('Less than: ', x < y)\n", "print('Greater than or equal: ', x >= y)\n", "print('Negation: ', not x >= y)\n", "print('Composition: ', ((x > y) and (y < 5)) or (x > 5))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Data containers" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "\n", "Data containers are used to group variables and literals. \n", "\n", "Four major container types are:\n", "- lists\n", "- tuples\n", "- sets \n", "- dictionaries" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Lists\n", "List are mutable (the number of their elements can change after they are created) and ordered data containers which can hold any data type." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "grocery = ['milk', 'wine', 'cheese'] \n", "random_stuff = ['ABC', 3.14, None, grocery] \n", "energies = [0.1, 0.2, 0.3]\n", "\n", "type(random_stuff) " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Appending lists" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "fibonacci = []\n", "fibonacci.append(0)\n", "fibonacci.append(1)\n", "fibonacci.append(1)\n", "fibonacci.append(2)\n", "print(fibonacci)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Extending lists" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "grocery = ['bread', 'soup', 'cheese']\n", "drinks = ['beer', 'wine', 'soda']\n", "grocery.extend(drinks)\n", "print(grocery)\n", "print(len(grocery))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Selecting list elements" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "grocery = ['bread', 'soup', 'cheese', 'beer', 'wine', 'soda']\n", "print(grocery[0])\n", "print(grocery[1], grocery[-1])\n", "print(grocery[1:3])" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Changing list elements " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "grocery = ['milk', 'soup', 'cheese']\n", "print(grocery)\n", "grocery[2] = 'coffee' # Change the third element\n", "print(grocery)\n", "del grocery[1] # Delete the second element\n", "print(grocery)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Tuples\n", "\n", "- Tuples are similar to lists, but they are immutable.\n", "- Their elements can't change after a tuple is being created.\n", "- They are set with commas, with or without parentheses. \n", "- Good for returning multiple values from a function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "days = 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'\n", "x = (1, 2, 3)\n", "days[1]" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Unpacking tuples" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "x = (2.5, 5.4, 0.5)\n", "energy, volume, bias = x\n", "print(volume)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Dictionaries\n", "Dictionaries are mutable collections of key/value pairs.\n", "\n", "\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "temp_F = {} # An empty dictionary\n", "temp_F['Boston'] = 70.\n", "temp_F['New York'] = 72. \n", "temp_F['Newark'] = 75. \n", "print(temp_F['Boston'])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "\n", "phone = {'Alice': '123-4567-890',\n", " 'Bob': '123-4444-843',\n", " 'Tom': '843-4342-434'}\n", "\n", "phone['Bob']" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Control flow\n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "#### The if statement\n", "\n", "Indentation plays a very important role in determining how code is executed. Same code with different indentation will do different things. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "x = 1 # Asignment\n", "if x == 1: # Conditional\n", " print('x is equal to one') # Beginning of a if-block\n", " x = 2 # End of the if-block. \n", "print(x) # This line is outside of the if-block " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### If..elif..else statements" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "element = 'C'\n", "\n", "if element == 'C': \n", " atm_number = 6\n", " name = 'carbon'\n", "elif element == 'O':\n", " atm_number = 8\n", " name = 'oxygen'\n", "else:\n", " atm_number = -1\n", " name = 'undefined'\n", "\n", "print('Element %d: %s' % (atm_number, name))" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Loops " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "energies = [0.1, 0.2, 0.3, 0.4] \n", "\n", "for energy in energies:\n", " print('The energy is %e ' % energy)\n", " print(\"End\")" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### while loops" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "code_folding": [], "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "count = 0\n", "while (count < 5):\n", " count = count + 1\n", " print(count)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Using function range() to iterate over integers\n", "\n", "NOTE: To create an array of floating point numbers it is better to use the numpy library." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "for i in range(3):\n", " print(i, i**2)\n", "\n", "for j in range(2, 20, 7):\n", " print('j = ', j)\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Using enumerate() to get the index or an element\n", "\n", "Enumerate returns next element and its index" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "chars = ['A', 'B', 'C', 'D']\n", "\n", "for index, e in enumerate(chars):\n", " print(index, e)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### List comprehensions\n", "\n", "Perform some operation on a list in a single line" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "potentials = [1.0, 2.0, 4.0, 4.0] \n", "\n", "pot2 = [pot**2 for pot in potentials]\n", "print(pot2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "out_files = ['pot_%0.1f.txt' % pot for pot in potentials]\n", "print(out_files)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Functions \n" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Functions without arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def some_function():\n", " print(\"Hello\")\n", " x = 2\n", " return # By default function returns None\n", "\n", "print(some_function())" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Functions with positional arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def add(x, y):\n", " result = x + y\n", " return result\n", "\n", "add(5, 7.2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [], "source": [ "def circle(x, y):\n", " radius = 8.0\n", " in_circle = (x**2 + y**2 < radius**2)\n", " return in_circle\n", "\n", "circle(0, 7)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Functions with keyword arguments" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "\n", "def circle(x, y, x0=0, y0=0, radius=8):\n", " in_circle = ((x-x0)**2 + (y-y0)**2 < radius**2)\n", " return in_circle \n", "\n", "circle(2, 3) # You have to supply the positional arguments\n", " " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "circle(2, 3, 7, 5, 6) # Without keywords, the argument ordering follows the ordering in the definition\n", "circle(2, 3, radius=10) # Changing a single keyword argument\n", "circle(2, 3, y0=5, x0=2) # You can change the order of keyword arguments\n", "circle(2, 3, 7, radius=2) # Here 7 is assigned to x0, y0 is 0, and radius is 2\n", "# circle(2, 3, radius=2, 5) # Error: Non-keyword argument can't follow a keyword argument" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Functions returning multiple values" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def f(x):\n", " return x, x**2, x**3\n", "\n", "a, b, c = f(3)\n", "print(a, b, c)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Built-in math functions\n", "\n", "NOTE: Most of these functions operate on a single number. There are their\n", "alternatives in numpy package which operate on entire arrays." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "from math import sin, cos, exp, tan\n", "from math import pi \n", "\n", "alpha = 30.0 \n", "alpha_rad = alpha * pi / 180\n", "sin(alpha_rad), cos(alpha_rad), exp(alpha_rad), abs(-5)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "# Pitfalls and error messages" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "When you start writing your code, in the beginning, it is more likely it will fail than work. Python will produce error messages to help you find where the problem is and remove it. That is why it is important to read and understand the different types of error messages." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "There are eight standard error types in Python. \n", " - Indentation error\n", " - Syntax error\n", " - Name error\n", " - Type error\n", " - Input-output error\n", " - Key error\n", " - Index error\n", " - Attribute error\n", "\n", "\n", "Once you become more experienced with the language, you can define new error types yourself. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Indentation error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "Four space characters must indent the code inside of an if-block, for-loop or a function. If any line of your code is not indented by a multiple of four characters, the interpreter might get confused, and it will throw you an indentation error." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "numbers = [1, 2, 5, 7]\n", "\n", "for n in numbers:\n", " d = n**2\n", " print(d) # Neither in the loop nor outside of it \n", "print(\"Done\") " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Syntax error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "You wrote something which is not a Python statement. These errors usually occur when you have a typo in your code (missing colon symbol ':', missing brackets, or missing quotation symbol)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def square(a) # This line is missing a colon\n", " return a**2\n", "square(3)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Name error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "skip" } }, "source": [ "Python can't associate the word you typed with a known function, module, or a variable. This error happens if you mistype a variable or function name. Another case is if you call a function (or a variable) that is not defined in the present scope. " ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def to_radian(angle):\n", " return angle * 3.141592 / 180.\n", "\n", "to_radians(75) # An extra 's' at the end\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Type error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "You are trying to perform some operation on a type that doesn't support that operation. This might happen if you supply the wrong type as an argument to a function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "def square(a):\n", " s = a**2 # You can't compute a power of a list\n", " print('The square is ', s)\n", " return s\n", "\n", "x = [5] \n", "square(x) # Function expects int or float, not a list" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### IO Error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "You want to open a file, but the file doesn't exist." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Key error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "-" } }, "source": [ "You want to select an undefined dictionary entry." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "p_radius = {'Mercury': 1516., 'Venus': 3760.4, \n", " 'Earth' : 3958.8, 'Mars': 2106.1,\n", " 'Jupiter': 43441., 'Saturn': 36184., \n", " 'Uranus': 15759., 'Neptune': 15299.}\n", "\n", "p_radius['Sun'] " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Index error\n", "\n", "Index goes beyond the list size" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "a = ['A', 'B', 'C']\n", "a[3] # Indexing starts at zero, last index is 2" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Attribute error" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "notes" } }, "source": [ "You are trying to access an attribute that is not in a module or object." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "-" } }, "outputs": [], "source": [ "import numpy as np\n", "\n", "np.zeros(5)\n", "np.fibonacci(5) # This function is not defined in numpy" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Understanding Python aliases" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is important to emphasize that the assignment operator '=' sometimes works differently in Python than in other languages. If a variable is of mutable type (e.g., a list or a numpy array), the assignment operator will create an alias instead of a copy of the variable. An alias is just a different name for the same variable. One can use the built-in id() function to check for aliases." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = np.array([1, 2, 3])\n", "b = a # Creating an alias\n", "print('ID: ', id(a), id(b))\n", "b[1] = 100 # Caution! This will also modify array a\n", "\n", "print(a)\n", "print(b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aliases allow renaming of objects in Python. However, as shown in the previous example, they can create problems if one is not aware of them. To create a copy of a variable instead of an alias." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "a = np.array([1, 2, 3])\n", "b = np.array(a) # This will create a copy of a\n", "print('ID: ', id(a), id(b))\n", "b[1] = 100 # This doesn't influence a\n", "\n", "print(a)\n", "print(b)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## More information on Python\n", " - [Official documentation page](https://docs.python.org/3/)\n", " - [Style guide for Python code (PEP8)](https://www.python.org/dev/peps/pep-0008/)\n", " " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Please send corrections to petrovic@udel.edu" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "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.8.3" } }, "nbformat": 4, "nbformat_minor": 2 }