{ "cells": [ { "cell_type": "markdown", "id": "f75ae8d7", "metadata": {}, "source": [ "\n", "\n", "
\n", " \n", " \"QuantEcon\"\n", " \n", "
" ] }, { "cell_type": "markdown", "id": "42df66f4", "metadata": {}, "source": [ "# Python Essentials" ] }, { "cell_type": "markdown", "id": "326df403", "metadata": {}, "source": [ "## Contents\n", "\n", "- [Python Essentials](#Python-Essentials) \n", " - [Overview](#Overview) \n", " - [Data Types](#Data-Types) \n", " - [Input and Output](#Input-and-Output) \n", " - [Iterating](#Iterating) \n", " - [Comparisons and Logical Operators](#Comparisons-and-Logical-Operators) \n", " - [Coding Style and Documentation](#Coding-Style-and-Documentation) \n", " - [Exercises](#Exercises) " ] }, { "cell_type": "markdown", "id": "21f24726", "metadata": {}, "source": [ "## Overview\n", "\n", "We have covered a lot of material quite quickly, with a focus on examples.\n", "\n", "Now let’s cover some core features of Python in a more systematic way.\n", "\n", "This approach is less exciting but helps clear up some details." ] }, { "cell_type": "markdown", "id": "21ca590a", "metadata": {}, "source": [ "## Data Types\n", "\n", "\n", "\n", "Computer programs typically keep track of a range of data types.\n", "\n", "For example, `1.5` is a floating point number, while `1` is an integer.\n", "\n", "Programs need to distinguish between these two types for various reasons.\n", "\n", "One is that they are stored in memory differently.\n", "\n", "Another is that arithmetic operations are different\n", "\n", "- For example, floating point arithmetic is implemented on most machines by a\n", " specialized Floating Point Unit (FPU). \n", "\n", "\n", "In general, floats are more informative but arithmetic operations on integers\n", "are faster and more accurate.\n", "\n", "Python provides numerous other built-in Python data types, some of which we’ve already met\n", "\n", "- strings, lists, etc. \n", "\n", "\n", "Let’s learn a bit more about them." ] }, { "cell_type": "markdown", "id": "2f51ff11", "metadata": {}, "source": [ "### Primitive Data Types\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "id": "438ba7d6", "metadata": {}, "source": [ "#### Boolean Values\n", "\n", "One simple data type is **Boolean values**, which can be either `True` or `False`" ] }, { "cell_type": "code", "execution_count": null, "id": "0bdc3766", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = True\n", "x" ] }, { "cell_type": "markdown", "id": "623f1684", "metadata": {}, "source": [ "We can check the type of any object in memory using the `type()` function." ] }, { "cell_type": "code", "execution_count": null, "id": "b8f68ee9", "metadata": { "hide-output": false }, "outputs": [], "source": [ "type(x)" ] }, { "cell_type": "markdown", "id": "d47a999d", "metadata": {}, "source": [ "In the next line of code, the interpreter evaluates the expression on the right of = and binds y to this value" ] }, { "cell_type": "code", "execution_count": null, "id": "ca81af63", "metadata": { "hide-output": false }, "outputs": [], "source": [ "y = 100 < 10\n", "y" ] }, { "cell_type": "code", "execution_count": null, "id": "fd7bd572", "metadata": { "hide-output": false }, "outputs": [], "source": [ "type(y)" ] }, { "cell_type": "markdown", "id": "18788bf7", "metadata": {}, "source": [ "In arithmetic expressions, `True` is converted to `1` and `False` is converted `0`.\n", "\n", "This is called **Boolean arithmetic** and is often useful in programming.\n", "\n", "Here are some examples" ] }, { "cell_type": "code", "execution_count": null, "id": "02cd9463", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x + y" ] }, { "cell_type": "code", "execution_count": null, "id": "26ba6fea", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x * y" ] }, { "cell_type": "code", "execution_count": null, "id": "8a627c2f", "metadata": { "hide-output": false }, "outputs": [], "source": [ "True + True" ] }, { "cell_type": "code", "execution_count": null, "id": "f1571076", "metadata": { "hide-output": false }, "outputs": [], "source": [ "bools = [True, True, False, True] # List of Boolean values\n", "\n", "sum(bools)" ] }, { "cell_type": "markdown", "id": "0af05fb3", "metadata": {}, "source": [ "#### Numeric Types\n", "\n", "Numeric types are also important primitive data types.\n", "\n", "We have seen `integer` and `float` types before.\n", "\n", "**Complex numbers** are another primitive data type in Python" ] }, { "cell_type": "code", "execution_count": null, "id": "6aa21549", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = complex(1, 2)\n", "y = complex(2, 1)\n", "print(x * y)\n", "\n", "type(x)" ] }, { "cell_type": "markdown", "id": "a1dbff31", "metadata": {}, "source": [ "### Containers\n", "\n", "Python has several basic types for storing collections of (possibly heterogeneous) data.\n", "\n", "We’ve [already discussed lists](https://python-programming.quantecon.org/python_by_example.html#lists-ref).\n", "\n", "\n", "\n", "A related data type is **tuples**, which are “immutable” lists" ] }, { "cell_type": "code", "execution_count": null, "id": "bac76222", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = ('a', 'b') # Parentheses instead of the square brackets\n", "x = 'a', 'b' # Or no brackets --- the meaning is identical\n", "x" ] }, { "cell_type": "code", "execution_count": null, "id": "80cd188b", "metadata": { "hide-output": false }, "outputs": [], "source": [ "type(x)" ] }, { "cell_type": "markdown", "id": "73bbca93", "metadata": {}, "source": [ "In Python, an object is called **immutable** if, once created, the object cannot be changed.\n", "\n", "Conversely, an object is **mutable** if it can still be altered after creation.\n", "\n", "Python lists are mutable" ] }, { "cell_type": "code", "execution_count": null, "id": "c9bdac04", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = [1, 2]\n", "x[0] = 10\n", "x" ] }, { "cell_type": "markdown", "id": "0bd7414c", "metadata": {}, "source": [ "But tuples are not" ] }, { "cell_type": "code", "execution_count": null, "id": "664b2098", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = (1, 2)\n", "x[0] = 10" ] }, { "cell_type": "markdown", "id": "cb633412", "metadata": {}, "source": [ "We’ll say more about the role of mutable and immutable data a bit later.\n", "\n", "Tuples (and lists) can be “unpacked” as follows" ] }, { "cell_type": "code", "execution_count": null, "id": "56c4b614", "metadata": { "hide-output": false }, "outputs": [], "source": [ "integers = (10, 20, 30)\n", "x, y, z = integers\n", "x" ] }, { "cell_type": "code", "execution_count": null, "id": "bc915a75", "metadata": { "hide-output": false }, "outputs": [], "source": [ "y" ] }, { "cell_type": "markdown", "id": "ee153aa8", "metadata": {}, "source": [ "You’ve actually [seen an example of this](https://python-programming.quantecon.org/about_py.html#tuple-unpacking-example) already.\n", "\n", "Tuple unpacking is convenient and we’ll use it often." ] }, { "cell_type": "markdown", "id": "b47de582", "metadata": {}, "source": [ "#### Slice Notation\n", "\n", "\n", "\n", "To access multiple elements of a sequence (a list, a tuple or a string), you can use Python’s slice\n", "notation.\n", "\n", "For example," ] }, { "cell_type": "code", "execution_count": null, "id": "b3784029", "metadata": { "hide-output": false }, "outputs": [], "source": [ "a = [\"a\", \"b\", \"c\", \"d\", \"e\"]\n", "a[1:]" ] }, { "cell_type": "code", "execution_count": null, "id": "e8e8adaf", "metadata": { "hide-output": false }, "outputs": [], "source": [ "a[1:3]" ] }, { "cell_type": "markdown", "id": "ec6a0bfc", "metadata": {}, "source": [ "The general rule is that `a[m:n]` returns `n - m` elements, starting at `a[m]`.\n", "\n", "Negative numbers are also permissible" ] }, { "cell_type": "code", "execution_count": null, "id": "cf5ae83f", "metadata": { "hide-output": false }, "outputs": [], "source": [ "a[-2:] # Last two elements of the list" ] }, { "cell_type": "markdown", "id": "5151e3aa", "metadata": {}, "source": [ "You can also use the format `[start:end:step]` to specify the step" ] }, { "cell_type": "code", "execution_count": null, "id": "88a0d0af", "metadata": { "hide-output": false }, "outputs": [], "source": [ "a[::2]" ] }, { "cell_type": "markdown", "id": "3f0fe15b", "metadata": {}, "source": [ "Using a negative step, you can return the sequence in a reversed order" ] }, { "cell_type": "code", "execution_count": null, "id": "d42881fd", "metadata": { "hide-output": false }, "outputs": [], "source": [ "a[-2::-1] # Walk backwards from the second last element to the first element" ] }, { "cell_type": "markdown", "id": "096227e2", "metadata": {}, "source": [ "The same slice notation works on tuples and strings" ] }, { "cell_type": "code", "execution_count": null, "id": "6d0abfa2", "metadata": { "hide-output": false }, "outputs": [], "source": [ "s = 'foobar'\n", "s[-3:] # Select the last three elements" ] }, { "cell_type": "markdown", "id": "2bf4d924", "metadata": {}, "source": [ "#### Sets and Dictionaries\n", "\n", "\n", "\n", "Two other container types we should mention before moving on are [sets](https://docs.python.org/3/tutorial/datastructures.html#sets) and [dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries).\n", "\n", "Dictionaries are much like lists, except that the items are named instead of\n", "numbered" ] }, { "cell_type": "code", "execution_count": null, "id": "df8a40e9", "metadata": { "hide-output": false }, "outputs": [], "source": [ "d = {'name': 'Frodo', 'age': 33}\n", "type(d)" ] }, { "cell_type": "code", "execution_count": null, "id": "190cdf75", "metadata": { "hide-output": false }, "outputs": [], "source": [ "d['age']" ] }, { "cell_type": "markdown", "id": "61c14b98", "metadata": {}, "source": [ "The names `'name'` and `'age'` are called the *keys*.\n", "\n", "The objects that the keys are mapped to (`'Frodo'` and `33`) are called the `values`.\n", "\n", "Sets are unordered collections without duplicates, and set methods provide the\n", "usual set-theoretic operations" ] }, { "cell_type": "code", "execution_count": null, "id": "79784e12", "metadata": { "hide-output": false }, "outputs": [], "source": [ "s1 = {'a', 'b'}\n", "type(s1)" ] }, { "cell_type": "code", "execution_count": null, "id": "b3d11c77", "metadata": { "hide-output": false }, "outputs": [], "source": [ "s2 = {'b', 'c'}\n", "s1.issubset(s2)" ] }, { "cell_type": "code", "execution_count": null, "id": "1f2438a7", "metadata": { "hide-output": false }, "outputs": [], "source": [ "s1.intersection(s2)" ] }, { "cell_type": "markdown", "id": "f1a3d8eb", "metadata": {}, "source": [ "The `set()` function creates sets from sequences" ] }, { "cell_type": "code", "execution_count": null, "id": "b94692c5", "metadata": { "hide-output": false }, "outputs": [], "source": [ "s3 = set(('foo', 'bar', 'foo'))\n", "s3" ] }, { "cell_type": "markdown", "id": "2c05321e", "metadata": {}, "source": [ "## Input and Output\n", "\n", "\n", "\n", "Let’s briefly review reading and writing to text files, starting with writing" ] }, { "cell_type": "code", "execution_count": null, "id": "27a8a02e", "metadata": { "hide-output": false }, "outputs": [], "source": [ "f = open('newfile.txt', 'w') # Open 'newfile.txt' for writing\n", "f.write('Testing\\n') # Here '\\n' means new line\n", "f.write('Testing again')\n", "f.close()" ] }, { "cell_type": "markdown", "id": "5d1c78ea", "metadata": {}, "source": [ "Here\n", "\n", "- The built-in function `open()` creates a file object for writing to. \n", "- Both `write()` and `close()` are methods of file objects. \n", "\n", "\n", "Where is this file that we’ve created?\n", "\n", "Recall that Python maintains a concept of the present working directory (pwd) that can be located from with Jupyter or IPython via" ] }, { "cell_type": "code", "execution_count": null, "id": "9598b49f", "metadata": { "hide-output": false }, "outputs": [], "source": [ "%pwd" ] }, { "cell_type": "markdown", "id": "bc68fd2c", "metadata": {}, "source": [ "If a path is not specified, then this is where Python writes to.\n", "\n", "We can also use Python to read the contents of `newline.txt` as follows" ] }, { "cell_type": "code", "execution_count": null, "id": "699a4c98", "metadata": { "hide-output": false }, "outputs": [], "source": [ "f = open('newfile.txt', 'r')\n", "out = f.read()\n", "out" ] }, { "cell_type": "code", "execution_count": null, "id": "e1babedd", "metadata": { "hide-output": false }, "outputs": [], "source": [ "print(out)" ] }, { "cell_type": "markdown", "id": "5c7cf863", "metadata": {}, "source": [ "In fact, the recommended approach in modern Python is to use a `with` statement to ensure the files are properly acquired and released.\n", "\n", "Containing the operations within the same block also improves the clarity of your code.\n", "\n", ">**Note**\n", ">\n", ">This kind of block is formally referred to as a [*context*](https://realpython.com/python-with-statement/#the-with-statement-approach).\n", "\n", "Let’s try to convert the two examples above into a `with` statement.\n", "\n", "We change the writing example first" ] }, { "cell_type": "code", "execution_count": null, "id": "eabe6e5e", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open('newfile.txt', 'w') as f: \n", " f.write('Testing\\n') \n", " f.write('Testing again')" ] }, { "cell_type": "markdown", "id": "6eb7da5c", "metadata": {}, "source": [ "Note that we do not need to call the `close()` method since the `with` block\n", "will ensure the stream is closed at the end of the block.\n", "\n", "With slight modifications, we can also read files using `with`" ] }, { "cell_type": "code", "execution_count": null, "id": "de91bfde", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open('newfile.txt', 'r') as fo:\n", " out = fo.read()\n", " print(out)" ] }, { "cell_type": "markdown", "id": "3c72086a", "metadata": {}, "source": [ "Now suppose that we want to read input from one file and write output to another.\n", "Here’s how we could accomplish this task while correctly acquiring and returning\n", "resources to the operating system using `with` statements:" ] }, { "cell_type": "code", "execution_count": null, "id": "ab14136c", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open(\"newfile.txt\", \"r\") as f:\n", " file = f.readlines()\n", " with open(\"output.txt\", \"w\") as fo:\n", " for i, line in enumerate(file):\n", " fo.write(f'Line {i}: {line} \\n')" ] }, { "cell_type": "markdown", "id": "72d5365b", "metadata": {}, "source": [ "The output file will be" ] }, { "cell_type": "code", "execution_count": null, "id": "41c4d4d9", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open('output.txt', 'r') as fo:\n", " print(fo.read())" ] }, { "cell_type": "markdown", "id": "f381f63d", "metadata": {}, "source": [ "We can simplify the example above by grouping the two `with` statements into one line" ] }, { "cell_type": "code", "execution_count": null, "id": "7bddfbad", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open(\"newfile.txt\", \"r\") as f, open(\"output2.txt\", \"w\") as fo:\n", " for i, line in enumerate(f):\n", " fo.write(f'Line {i}: {line} \\n')" ] }, { "cell_type": "markdown", "id": "e9a45992", "metadata": {}, "source": [ "The output file will be the same" ] }, { "cell_type": "code", "execution_count": null, "id": "5d97010b", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open('output2.txt', 'r') as fo:\n", " print(fo.read())" ] }, { "cell_type": "markdown", "id": "4307b0ce", "metadata": {}, "source": [ "Suppose we want to continue to write into the existing file\n", "instead of overwriting it.\n", "\n", "we can switch the mode to `a` which stands for append mode" ] }, { "cell_type": "code", "execution_count": null, "id": "d7fab071", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open('output2.txt', 'a') as fo:\n", " fo.write('\\nThis is the end of the file')" ] }, { "cell_type": "code", "execution_count": null, "id": "894cab8d", "metadata": { "hide-output": false }, "outputs": [], "source": [ "with open('output2.txt', 'r') as fo:\n", " print(fo.read())" ] }, { "cell_type": "markdown", "id": "31023fc4", "metadata": {}, "source": [ ">**Note**\n", ">\n", ">Note that we only covered `r`, `w`, and `a` mode here, which are the most commonly used modes.\n", "Python provides [a variety of modes](https://www.geeksforgeeks.org/reading-writing-text-files-python/)\n", "that you could experiment with." ] }, { "cell_type": "markdown", "id": "1232d391", "metadata": {}, "source": [ "### Paths\n", "\n", "\n", "\n", "Note that if `newfile.txt` is not in the present working directory then this call to `open()` fails.\n", "\n", "In this case, you can shift the file to the pwd or specify the [full path](https://en.wikipedia.org/wiki/Path_%28computing%29) to the file" ] }, { "cell_type": "markdown", "id": "7ea4dd79", "metadata": { "hide-output": false }, "source": [ "```python3\n", "f = open('insert_full_path_to_file/newfile.txt', 'r')\n", "```\n" ] }, { "cell_type": "markdown", "id": "1ccff666", "metadata": {}, "source": [ "\n", "" ] }, { "cell_type": "markdown", "id": "e2b1cedc", "metadata": {}, "source": [ "## Iterating\n", "\n", "\n", "\n", "One of the most important tasks in computing is stepping through a\n", "sequence of data and performing a given action.\n", "\n", "One of Python’s strengths is its simple, flexible interface to this kind of iteration via\n", "the `for` loop." ] }, { "cell_type": "markdown", "id": "52dacb45", "metadata": {}, "source": [ "### Looping over Different Objects\n", "\n", "Many Python objects are “iterable”, in the sense that they can be looped over.\n", "\n", "To give an example, let’s write the file us_cities.txt, which lists US cities and their population, to the present working directory.\n", "\n", "\n", "" ] }, { "cell_type": "code", "execution_count": null, "id": "2d0b806d", "metadata": { "hide-output": false }, "outputs": [], "source": [ "%%writefile us_cities.txt\n", "new york: 8244910\n", "los angeles: 3819702\n", "chicago: 2707120\n", "houston: 2145146\n", "philadelphia: 1536471\n", "phoenix: 1469471\n", "san antonio: 1359758\n", "san diego: 1326179\n", "dallas: 1223229" ] }, { "cell_type": "markdown", "id": "4b1ccd4a", "metadata": {}, "source": [ "Here `%%writefile` is an [IPython cell magic](https://ipython.readthedocs.io/en/stable/interactive/magics.html#cell-magics).\n", "\n", "Suppose that we want to make the information more readable, by capitalizing names and adding commas to mark thousands.\n", "\n", "The program below reads the data in and makes the conversion:" ] }, { "cell_type": "code", "execution_count": null, "id": "81f8f99e", "metadata": { "hide-output": false }, "outputs": [], "source": [ "data_file = open('us_cities.txt', 'r')\n", "for line in data_file:\n", " city, population = line.split(':') # Tuple unpacking\n", " city = city.title() # Capitalize city names\n", " population = f'{int(population):,}' # Add commas to numbers\n", " print(city.ljust(15) + population)\n", "data_file.close()" ] }, { "cell_type": "markdown", "id": "443b906e", "metadata": {}, "source": [ "Here `format()` is a string method [used for inserting variables into strings](https://docs.python.org/3/library/string.html#formatspec).\n", "\n", "The reformatting of each line is the result of three different string methods,\n", "the details of which can be left till later.\n", "\n", "The interesting part of this program for us is line 2, which shows that\n", "\n", "1. The file object `data_file` is iterable, in the sense that it can be placed to the right of `in` within a `for` loop. \n", "1. Iteration steps through each line in the file. \n", "\n", "\n", "This leads to the clean, convenient syntax shown in our program.\n", "\n", "Many other kinds of objects are iterable, and we’ll discuss some of them later on." ] }, { "cell_type": "markdown", "id": "451776a4", "metadata": {}, "source": [ "### Looping without Indices\n", "\n", "One thing you might have noticed is that Python tends to favor looping without explicit indexing.\n", "\n", "For example," ] }, { "cell_type": "code", "execution_count": null, "id": "a00ed92c", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x_values = [1, 2, 3] # Some iterable x\n", "for x in x_values:\n", " print(x * x)" ] }, { "cell_type": "markdown", "id": "ae932625", "metadata": {}, "source": [ "is preferred to" ] }, { "cell_type": "code", "execution_count": null, "id": "fcbe9227", "metadata": { "hide-output": false }, "outputs": [], "source": [ "for i in range(len(x_values)):\n", " print(x_values[i] * x_values[i])" ] }, { "cell_type": "markdown", "id": "a910e372", "metadata": {}, "source": [ "When you compare these two alternatives, you can see why the first one is preferred.\n", "\n", "Python provides some facilities to simplify looping without indices.\n", "\n", "One is `zip()`, which is used for stepping through pairs from two sequences.\n", "\n", "For example, try running the following code" ] }, { "cell_type": "code", "execution_count": null, "id": "b00e2502", "metadata": { "hide-output": false }, "outputs": [], "source": [ "countries = ('Japan', 'Korea', 'China')\n", "cities = ('Tokyo', 'Seoul', 'Beijing')\n", "for country, city in zip(countries, cities):\n", " print(f'The capital of {country} is {city}')" ] }, { "cell_type": "markdown", "id": "66dba4b3", "metadata": {}, "source": [ "The `zip()` function is also useful for creating dictionaries — for\n", "example" ] }, { "cell_type": "code", "execution_count": null, "id": "9670c7d3", "metadata": { "hide-output": false }, "outputs": [], "source": [ "names = ['Tom', 'John']\n", "marks = ['E', 'F']\n", "dict(zip(names, marks))" ] }, { "cell_type": "markdown", "id": "6cc82031", "metadata": {}, "source": [ "If we actually need the index from a list, one option is to use `enumerate()`.\n", "\n", "To understand what `enumerate()` does, consider the following example" ] }, { "cell_type": "code", "execution_count": null, "id": "b814078e", "metadata": { "hide-output": false }, "outputs": [], "source": [ "letter_list = ['a', 'b', 'c']\n", "for index, letter in enumerate(letter_list):\n", " print(f\"letter_list[{index}] = '{letter}'\")" ] }, { "cell_type": "markdown", "id": "d5ad9f95", "metadata": {}, "source": [ "\n", "" ] }, { "cell_type": "markdown", "id": "eb1fd38b", "metadata": {}, "source": [ "### List Comprehensions\n", "\n", "\n", "\n", "We can also simplify the code for generating the list of random draws considerably by using something called a *list comprehension*.\n", "\n", "[List comprehensions](https://en.wikipedia.org/wiki/List_comprehension) are an elegant Python tool for creating lists.\n", "\n", "Consider the following example, where the list comprehension is on the\n", "right-hand side of the second line" ] }, { "cell_type": "code", "execution_count": null, "id": "1e0cf434", "metadata": { "hide-output": false }, "outputs": [], "source": [ "animals = ['dog', 'cat', 'bird']\n", "plurals = [animal + 's' for animal in animals]\n", "plurals" ] }, { "cell_type": "markdown", "id": "c19859c4", "metadata": {}, "source": [ "Here’s another example" ] }, { "cell_type": "code", "execution_count": null, "id": "21977a9b", "metadata": { "hide-output": false }, "outputs": [], "source": [ "range(8)" ] }, { "cell_type": "code", "execution_count": null, "id": "e3cb1e1b", "metadata": { "hide-output": false }, "outputs": [], "source": [ "doubles = [2 * x for x in range(8)]\n", "doubles" ] }, { "cell_type": "markdown", "id": "248ea801", "metadata": {}, "source": [ "## Comparisons and Logical Operators" ] }, { "cell_type": "markdown", "id": "b3812777", "metadata": {}, "source": [ "### Comparisons\n", "\n", "\n", "\n", "Many different kinds of expressions evaluate to one of the Boolean values (i.e., `True` or `False`).\n", "\n", "A common type is comparisons, such as" ] }, { "cell_type": "code", "execution_count": null, "id": "146c3cc9", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x, y = 1, 2\n", "x < y" ] }, { "cell_type": "code", "execution_count": null, "id": "9abe046e", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x > y" ] }, { "cell_type": "markdown", "id": "cd5d7bab", "metadata": {}, "source": [ "One of the nice features of Python is that we can *chain* inequalities" ] }, { "cell_type": "code", "execution_count": null, "id": "3a6ad4e4", "metadata": { "hide-output": false }, "outputs": [], "source": [ "1 < 2 < 3" ] }, { "cell_type": "code", "execution_count": null, "id": "c4cbabe6", "metadata": { "hide-output": false }, "outputs": [], "source": [ "1 <= 2 <= 3" ] }, { "cell_type": "markdown", "id": "df4612a4", "metadata": {}, "source": [ "As we saw earlier, when testing for equality we use `==`" ] }, { "cell_type": "code", "execution_count": null, "id": "73a35531", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = 1 # Assignment\n", "x == 2 # Comparison" ] }, { "cell_type": "markdown", "id": "c5a45af3", "metadata": {}, "source": [ "For “not equal” use `!=`" ] }, { "cell_type": "code", "execution_count": null, "id": "37fad3f9", "metadata": { "hide-output": false }, "outputs": [], "source": [ "1 != 2" ] }, { "cell_type": "markdown", "id": "3a50fb28", "metadata": {}, "source": [ "Note that when testing conditions, we can use **any** valid Python expression" ] }, { "cell_type": "code", "execution_count": null, "id": "f3f4396a", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = 'yes' if 42 else 'no'\n", "x" ] }, { "cell_type": "code", "execution_count": null, "id": "42bb66a9", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x = 'yes' if [] else 'no'\n", "x" ] }, { "cell_type": "markdown", "id": "2306d692", "metadata": {}, "source": [ "What’s going on here?\n", "\n", "The rule is:\n", "\n", "- Expressions that evaluate to zero, empty sequences or containers (strings, lists, etc.) and `None` are all equivalent to `False`. \n", " - for example, `[]` and `()` are equivalent to `False` in an `if` clause \n", "- All other values are equivalent to `True`. \n", " - for example, `42` is equivalent to `True` in an `if` clause " ] }, { "cell_type": "markdown", "id": "52060ed2", "metadata": {}, "source": [ "### Combining Expressions\n", "\n", "\n", "\n", "We can combine expressions using `and`, `or` and `not`.\n", "\n", "These are the standard logical connectives (conjunction, disjunction and denial)" ] }, { "cell_type": "code", "execution_count": null, "id": "0a887c66", "metadata": { "hide-output": false }, "outputs": [], "source": [ "1 < 2 and 'f' in 'foo'" ] }, { "cell_type": "code", "execution_count": null, "id": "694df409", "metadata": { "hide-output": false }, "outputs": [], "source": [ "1 < 2 and 'g' in 'foo'" ] }, { "cell_type": "code", "execution_count": null, "id": "5ca9c295", "metadata": { "hide-output": false }, "outputs": [], "source": [ "1 < 2 or 'g' in 'foo'" ] }, { "cell_type": "code", "execution_count": null, "id": "593b9704", "metadata": { "hide-output": false }, "outputs": [], "source": [ "not True" ] }, { "cell_type": "code", "execution_count": null, "id": "8c648092", "metadata": { "hide-output": false }, "outputs": [], "source": [ "not not True" ] }, { "cell_type": "markdown", "id": "fb1658ba", "metadata": {}, "source": [ "Remember\n", "\n", "- `P and Q` is `True` if both are `True`, else `False` \n", "- `P or Q` is `False` if both are `False`, else `True` \n", "\n", "\n", "We can also use `all()` and `any()` to test a sequence of expressions" ] }, { "cell_type": "code", "execution_count": null, "id": "c3440d22", "metadata": { "hide-output": false }, "outputs": [], "source": [ "all([1 <= 2 <= 3, 5 <= 6 <= 7])" ] }, { "cell_type": "code", "execution_count": null, "id": "ed160a4e", "metadata": { "hide-output": false }, "outputs": [], "source": [ "all([1 <= 2 <= 3, \"a\" in \"letter\"])" ] }, { "cell_type": "code", "execution_count": null, "id": "d9ce9207", "metadata": { "hide-output": false }, "outputs": [], "source": [ "any([1 <= 2 <= 3, \"a\" in \"letter\"])" ] }, { "cell_type": "markdown", "id": "74b926ae", "metadata": {}, "source": [ ">**Note**\n", ">\n", ">- `all()` returns `True` when *all* boolean values/expressions in the sequence are `True` \n", "- `any()` returns `True` when *any* boolean values/expressions in the sequence are `True` " ] }, { "cell_type": "markdown", "id": "aec283ad", "metadata": {}, "source": [ "## Coding Style and Documentation\n", "\n", "A consistent coding style and the use of\n", "documentation can make the code easier to understand and maintain." ] }, { "cell_type": "markdown", "id": "8f4d24e3", "metadata": {}, "source": [ "### Python Style Guidelines: PEP8\n", "\n", "\n", "\n", "You can find Python programming philosophy by typing `import this` at the prompt.\n", "\n", "Among other things, Python strongly favors consistency in programming style.\n", "\n", "We’ve all heard the saying about consistency and little minds.\n", "\n", "In programming, as in mathematics, the opposite is true\n", "\n", "- A mathematical paper where the symbols $ \\cup $ and $ \\cap $ were\n", " reversed would be very hard to read, even if the author told you so on the\n", " first page. \n", "\n", "\n", "In Python, the standard style is set out in [PEP8](https://www.python.org/dev/peps/pep-0008/).\n", "\n", "(Occasionally we’ll deviate from PEP8 in these lectures to better match mathematical notation)\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "id": "02e3753a", "metadata": {}, "source": [ "### Docstrings\n", "\n", "\n", "\n", "Python has a system for adding comments to modules, classes, functions, etc. called *docstrings*.\n", "\n", "The nice thing about docstrings is that they are available at run-time.\n", "\n", "Try running this" ] }, { "cell_type": "code", "execution_count": null, "id": "9992d485", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def f(x):\n", " \"\"\"\n", " This function squares its argument\n", " \"\"\"\n", " return x**2" ] }, { "cell_type": "markdown", "id": "be7f53cc", "metadata": {}, "source": [ "After running this code, the docstring is available" ] }, { "cell_type": "code", "execution_count": null, "id": "dd5010b7", "metadata": { "hide-output": false }, "outputs": [], "source": [ "f?" ] }, { "cell_type": "markdown", "id": "e567f15e", "metadata": { "hide-output": false }, "source": [ "```ipython\n", "Type: function\n", "String Form:\n", "File: /home/john/temp/temp.py\n", "Definition: f(x)\n", "Docstring: This function squares its argument\n", "```\n" ] }, { "cell_type": "code", "execution_count": null, "id": "0652dc26", "metadata": { "hide-output": false }, "outputs": [], "source": [ "f??" ] }, { "cell_type": "markdown", "id": "eb25c023", "metadata": { "hide-output": false }, "source": [ "```ipython\n", "Type: function\n", "String Form:\n", "File: /home/john/temp/temp.py\n", "Definition: f(x)\n", "Source:\n", "def f(x):\n", " \"\"\"\n", " This function squares its argument\n", " \"\"\"\n", " return x**2\n", "```\n" ] }, { "cell_type": "markdown", "id": "01efee7f", "metadata": {}, "source": [ "With one question mark we bring up the docstring, and with two we get the source code as well.\n", "\n", "You can find conventions for docstrings in [PEP257](https://peps.python.org/pep-0257/)." ] }, { "cell_type": "markdown", "id": "f114fa80", "metadata": {}, "source": [ "## Exercises\n", "\n", "Solve the following exercises.\n", "\n", "(For some, the built-in function `sum()` comes in handy)." ] }, { "cell_type": "markdown", "id": "845adcda", "metadata": {}, "source": [ "## Exercise 5.1\n", "\n", "Part 1: Given two numeric lists or tuples `x_vals` and `y_vals` of equal length, compute\n", "their inner product using `zip()`.\n", "\n", "Part 2: In one line, count the number of even numbers in 0,…,99.\n", "\n", "Part 3: Given `pairs = ((2, 5), (4, 2), (9, 8), (12, 10))`, count the number of pairs `(a, b)`\n", "such that both `a` and `b` are even.\n", "\n", "`x % 2` returns 0 if `x` is even, 1 otherwise." ] }, { "cell_type": "markdown", "id": "17f57ee2", "metadata": {}, "source": [ "## Solution to[ Exercise 5.1](https://python-programming.quantecon.org/#pyess_ex1)\n", "\n", "**Part 1 Solution:**\n", "\n", "Here’s one possible solution" ] }, { "cell_type": "code", "execution_count": null, "id": "cbe3c1cb", "metadata": { "hide-output": false }, "outputs": [], "source": [ "x_vals = [1, 2, 3]\n", "y_vals = [1, 1, 1]\n", "sum([x * y for x, y in zip(x_vals, y_vals)])" ] }, { "cell_type": "markdown", "id": "519091ea", "metadata": {}, "source": [ "This also works" ] }, { "cell_type": "code", "execution_count": null, "id": "a5f134ed", "metadata": { "hide-output": false }, "outputs": [], "source": [ "sum(x * y for x, y in zip(x_vals, y_vals))" ] }, { "cell_type": "markdown", "id": "e107f329", "metadata": {}, "source": [ "**Part 2 Solution:**\n", "\n", "One solution is" ] }, { "cell_type": "code", "execution_count": null, "id": "233da4e1", "metadata": { "hide-output": false }, "outputs": [], "source": [ "sum([x % 2 == 0 for x in range(100)])" ] }, { "cell_type": "markdown", "id": "39f58c88", "metadata": {}, "source": [ "This also works:" ] }, { "cell_type": "code", "execution_count": null, "id": "c14db50f", "metadata": { "hide-output": false }, "outputs": [], "source": [ "sum(x % 2 == 0 for x in range(100))" ] }, { "cell_type": "markdown", "id": "c215949e", "metadata": {}, "source": [ "Some less natural alternatives that nonetheless help to illustrate the\n", "flexibility of list comprehensions are" ] }, { "cell_type": "code", "execution_count": null, "id": "9320daa4", "metadata": { "hide-output": false }, "outputs": [], "source": [ "len([x for x in range(100) if x % 2 == 0])" ] }, { "cell_type": "markdown", "id": "24955d77", "metadata": {}, "source": [ "and" ] }, { "cell_type": "code", "execution_count": null, "id": "5e2cd65b", "metadata": { "hide-output": false }, "outputs": [], "source": [ "sum([1 for x in range(100) if x % 2 == 0])" ] }, { "cell_type": "markdown", "id": "c0747a0d", "metadata": {}, "source": [ "**Part 3 Solution:**\n", "\n", "Here’s one possibility" ] }, { "cell_type": "code", "execution_count": null, "id": "edde9539", "metadata": { "hide-output": false }, "outputs": [], "source": [ "pairs = ((2, 5), (4, 2), (9, 8), (12, 10))\n", "sum([x % 2 == 0 and y % 2 == 0 for x, y in pairs])" ] }, { "cell_type": "markdown", "id": "fda046c1", "metadata": {}, "source": [ "## Exercise 5.2\n", "\n", "Consider the polynomial\n", "\n", "\n", "\n", "$$\n", "p(x)\n", "= a_0 + a_1 x + a_2 x^2 + \\cdots a_n x^n\n", "= \\sum_{i=0}^n a_i x^i \\tag{5.1}\n", "$$\n", "\n", "Write a function `p` such that `p(x, coeff)` that computes the value in [(5.1)](#equation-polynom0) given a point `x` and a list of coefficients `coeff` ($ a_1, a_2, \\cdots a_n $).\n", "\n", "Try to use `enumerate()` in your loop." ] }, { "cell_type": "markdown", "id": "967d476b", "metadata": {}, "source": [ "## Solution to[ Exercise 5.2](https://python-programming.quantecon.org/#pyess_ex2)\n", "\n", "Here’s a solution:" ] }, { "cell_type": "code", "execution_count": null, "id": "96092abe", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def p(x, coeff):\n", " return sum(a * x**i for i, a in enumerate(coeff))" ] }, { "cell_type": "code", "execution_count": null, "id": "ccc961cf", "metadata": { "hide-output": false }, "outputs": [], "source": [ "p(1, (2, 4))" ] }, { "cell_type": "markdown", "id": "d3bdef71", "metadata": {}, "source": [ "## Exercise 5.3\n", "\n", "Write a function that takes a string as an argument and returns the number of capital letters in the string.\n", "\n", "`'foo'.upper()` returns `'FOO'`." ] }, { "cell_type": "markdown", "id": "af992f08", "metadata": {}, "source": [ "## Solution to[ Exercise 5.3](https://python-programming.quantecon.org/#pyess_ex3)\n", "\n", "Here’s one solution:" ] }, { "cell_type": "code", "execution_count": null, "id": "e8cf06a7", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def f(string):\n", " count = 0\n", " for letter in string:\n", " if letter == letter.upper() and letter.isalpha():\n", " count += 1\n", " return count\n", "\n", "f('The Rain in Spain')" ] }, { "cell_type": "markdown", "id": "c858a020", "metadata": {}, "source": [ "An alternative, more pythonic solution:" ] }, { "cell_type": "code", "execution_count": null, "id": "27fe82e4", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def count_uppercase_chars(s):\n", " return sum([c.isupper() for c in s])\n", "\n", "count_uppercase_chars('The Rain in Spain')" ] }, { "cell_type": "markdown", "id": "fdeb8836", "metadata": {}, "source": [ "## Exercise 5.4\n", "\n", "Write a function that takes two sequences `seq_a` and `seq_b` as arguments and\n", "returns `True` if every element in `seq_a` is also an element of `seq_b`, else\n", "`False`.\n", "\n", "- By “sequence” we mean a list, a tuple or a string. \n", "- Do the exercise without using [sets](https://docs.python.org/3/tutorial/datastructures.html#sets) and set methods. " ] }, { "cell_type": "markdown", "id": "30ba221d", "metadata": {}, "source": [ "## Solution to[ Exercise 5.4](https://python-programming.quantecon.org/#pyess_ex4)\n", "\n", "Here’s a solution:" ] }, { "cell_type": "code", "execution_count": null, "id": "5efacfc1", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def f(seq_a, seq_b):\n", " for a in seq_a:\n", " if a not in seq_b:\n", " return False\n", " return True\n", "\n", "# == test == #\n", "print(f(\"ab\", \"cadb\"))\n", "print(f(\"ab\", \"cjdb\"))\n", "print(f([1, 2], [1, 2, 3]))\n", "print(f([1, 2, 3], [1, 2]))" ] }, { "cell_type": "markdown", "id": "8c842d83", "metadata": {}, "source": [ "An alternative, more pythonic solution using `all()`:" ] }, { "cell_type": "code", "execution_count": null, "id": "8672e884", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def f(seq_a, seq_b):\n", " return all([i in seq_b for i in seq_a])\n", "\n", "# == test == #\n", "print(f(\"ab\", \"cadb\"))\n", "print(f(\"ab\", \"cjdb\"))\n", "print(f([1, 2], [1, 2, 3]))\n", "print(f([1, 2, 3], [1, 2]))" ] }, { "cell_type": "markdown", "id": "d05c36a5", "metadata": {}, "source": [ "Of course, if we use the `sets` data type then the solution is easier" ] }, { "cell_type": "code", "execution_count": null, "id": "ac50f1cd", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def f(seq_a, seq_b):\n", " return set(seq_a).issubset(set(seq_b))" ] }, { "cell_type": "markdown", "id": "a2b32d7d", "metadata": {}, "source": [ "## Exercise 5.5\n", "\n", "When we cover the numerical libraries, we will see they include many\n", "alternatives for interpolation and function approximation.\n", "\n", "Nevertheless, let’s write our own function approximation routine as an exercise.\n", "\n", "In particular, without using any imports, write a function `linapprox` that takes as arguments\n", "\n", "- A function `f` mapping some interval $ [a, b] $ into $ \\mathbb R $. \n", "- Two scalars `a` and `b` providing the limits of this interval. \n", "- An integer `n` determining the number of grid points. \n", "- A number `x` satisfying `a <= x <= b`. \n", "\n", "\n", "and returns the [piecewise linear interpolation](https://en.wikipedia.org/wiki/Linear_interpolation) of `f` at `x`, based on `n` evenly spaced grid points `a = point[0] < point[1] < ... < point[n-1] = b`.\n", "\n", "Aim for clarity, not efficiency." ] }, { "cell_type": "markdown", "id": "c5ac268c", "metadata": {}, "source": [ "## Solution to[ Exercise 5.5](https://python-programming.quantecon.org/#pyess_ex5)\n", "\n", "Here’s a solution:" ] }, { "cell_type": "code", "execution_count": null, "id": "7421707b", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def linapprox(f, a, b, n, x):\n", " \"\"\"\n", " Evaluates the piecewise linear interpolant of f at x on the interval\n", " [a, b], with n evenly spaced grid points.\n", "\n", " Parameters\n", " ==========\n", " f : function\n", " The function to approximate\n", "\n", " x, a, b : scalars (floats or integers)\n", " Evaluation point and endpoints, with a <= x <= b\n", "\n", " n : integer\n", " Number of grid points\n", "\n", " Returns\n", " =======\n", " A float. The interpolant evaluated at x\n", "\n", " \"\"\"\n", " length_of_interval = b - a\n", " num_subintervals = n - 1\n", " step = length_of_interval / num_subintervals\n", "\n", " # === find first grid point larger than x === #\n", " point = a\n", " while point <= x:\n", " point += step\n", "\n", " # === x must lie between the gridpoints (point - step) and point === #\n", " u, v = point - step, point\n", "\n", " return f(u) + (x - u) * (f(v) - f(u)) / (v - u)" ] }, { "cell_type": "markdown", "id": "535ee934", "metadata": {}, "source": [ "## Exercise 5.6\n", "\n", "Using list comprehension syntax, we can simplify the loop in the following\n", "code." ] }, { "cell_type": "code", "execution_count": null, "id": "ef32d325", "metadata": { "hide-output": false }, "outputs": [], "source": [ "import numpy as np\n", "\n", "n = 100\n", "ϵ_values = []\n", "for i in range(n):\n", " e = np.random.randn()\n", " ϵ_values.append(e)" ] }, { "cell_type": "markdown", "id": "e74f53a6", "metadata": {}, "source": [ "## Solution to[ Exercise 5.6](https://python-programming.quantecon.org/#pyess_ex6)\n", "\n", "Here’s one solution." ] }, { "cell_type": "code", "execution_count": null, "id": "af5109cc", "metadata": { "hide-output": false }, "outputs": [], "source": [ "n = 100\n", "ϵ_values = [np.random.randn() for i in range(n)]" ] } ], "metadata": { "date": 1710455932.7876472, "filename": "python_essentials.md", "kernelspec": { "display_name": "Python", "language": "python3", "name": "python3" }, "title": "Python Essentials" }, "nbformat": 4, "nbformat_minor": 5 }