{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lesson 1b: Variables, operators, and types\n", "\n", "Whether you are programming in Python or pretty much any other language, you will be working with **variables**. While the precise definition of a variable will vary from language to language, we'll focus on Python variables here. Like many of the concepts in this course, though, the knowledge you gain about Python variables will translate to other languages.\n", "\n", "We will talk more about **objects** later, but a variable, like everything in Python, is an object. For now, you can think of it this way. The following can be properties of a variable:\n", "1. The **type** of variable. E.g., is it an integer, like `2`, or a string, like `'Hello, world.'`?\n", "2. The **value** of the variable.\n", "\n", "Depending on the type of the variable, you can do different things to it and other variables of similar type. This, as with most things, is best explored by example. We'll go through some of the properties of variables and things you can do to them in this tutorial." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Determining the type\n", "First, we will use Python's built-in `type()` function to determine the type of some variables." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(2)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "float" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(2.3)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "str" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type('Hello, world.')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `type` function told us that `2` is an `int` (short for integer), `2.3` is a `float` (short for floating point number, basically a real number that is not an integer), and `'Hello, world.'` is a `str` (short for string). Note that the single quotes around the characters indicate that it is a string. So, `'1'` is a string, but `1` is an integer.\n", "\n", "Note that we can also express `float`s using scientific notation; $4.5\\times 10^{-7}$ is expressed as `4.5e-7`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "float" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(4.5e-7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A note on strings\n", "\n", "We just saw that strings can be enclosed in single quotes. In Python, we can equivalently enclose them in double quotes. E.g.,\n", "\n", " 'my string'\n", "\n", "and\n", "\n", " \"my string\"\n", "\n", "are the same thing. We can also denote a string with triple quotes. So,\n", "\n", " \"\"\"my string\"\"\"\n", " '''my string'''\n", " \"my string\"\n", " 'my string'\n", " \n", "are all the same thing. The difference with triple quotes is that it allows a string to extend over multiple lines." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "It was the best of times,\n", "it was the worst of times...\n" ] } ], "source": [ "# A multi-line string\n", "my_str = \"\"\"It was the best of times,\n", "it was the worst of times...\"\"\"\n", "\n", "print(my_str)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note, though, we cannot do this with single quotes." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "ename": "SyntaxError", "evalue": "EOL while scanning string literal (, line 2)", "output_type": "error", "traceback": [ "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m2\u001b[0m\n\u001b[0;31m my_str = 'It was the best of times,\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m EOL while scanning string literal\n" ] } ], "source": [ "# This is a SyntaxError\n", "my_str = 'It was the best of times,\n", "it was the worst of times...'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arithmetic operators\n", "\n", "**Operators** allow you to do things with variables, like add them. They are represented by special symbols, like `+` and `*`. For now, we will focus on **arithmetic** operators. Python's arithmetic operators are\n", "\n", "|action|operator|\n", "|:-------|:----------:|\n", "|addition | `+`|\n", "|subtraction | `-`|\n", "|multiplication | `*`|\n", "|division | `/`|\n", "|raise to power | `**`|\n", "|modulo | `%`|\n", "|floor division | `//`|\n", "\n", "**Warning**: Do not use the `^` operator to raise to a power. That is actually the operator for bitwise XOR, which we will not cover in the course. Observe firey death if you use these inappropriately:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "194" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "10^200" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Instead of raising 10 to the 200th power, Python performed a bitwise XOR as illustrated below:\n", "\n", "| a |Binary|Decimal |\n", "|:------|:-------|:------- |\n", "|Input | `00001010` | `10` |\n", "|Input | `11001000` | `200` |\n", "|Output| `11000010` | `194` |\n", "\n", "*Note*: if you want to see how a decimal number is represented in binary, you can use the following:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'11000010'" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'{0:b}'.format(194)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operations on integers\n", "Let's see how these operators work on integers." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 + 3" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-1" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 - 3" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 * 3" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.6666666666666666" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 / 3" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2**3" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 % 3" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2 // 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operations on floats\n", "Let's try floats." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5.300000000000001" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 + 3.2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wait a minute! We know `2.1 + 3.2 = 5.3`, but Python gives `5.300000000000001`. This is due to the fact that floating point numbers are stored with a finite number of binary bits. There will always be some rounding errors. This means that as far as the computer is concerned, it cannot tell you that `2.1 + 3.2` and `5.3` are equal. This is important to remember when dealing with floats, as we will see in the next lesson." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-1.1" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 - 3.2" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "-8.881784197001252e-16" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Very very close to zero because of finite precision\n", "5.3 - (2.1 + 3.2)" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.720000000000001" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 * 3.2" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.65625" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 / 3.2" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "10.74241047739471" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1**3.2" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.1" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 % 3.2" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.0" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 // 3.2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aside from the floating point precision issue I already pointed out, everything is like we would expect. Note, though, that we cannot divide by zero." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "ename": "ZeroDivisionError", "evalue": "float division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m2.1\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0.0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: float division by zero" ] } ], "source": [ "2.1 / 0.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can't do it with `int`s, either." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "ename": "ZeroDivisionError", "evalue": "division by zero", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m2\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mZeroDivisionError\u001b[0m: division by zero" ] } ], "source": [ "2 / 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operations on integers and floats\n", "This proceeds as we think it should." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "2.1 + 3" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "2.1 - 3" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.300000000000001" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 * 3" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.7000000000000001" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 / 3" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9.261000000000001" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1**3" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.1" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 % 3" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "9.261000000000001" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1**3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And again we have the rounding errors, but everything is otherwise intuitive." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operations on strings\n", "\n", "Now let's try some of these operations on strings. This idea of applying mathematical operations to strings seems strange, but let's just mess around and see what we get." ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Hello, world.'" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Hello, ' + 'world.'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ah! Adding strings together concatenates them! This is also intuitive. How about subtracting strings?" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "ename": "TypeError", "evalue": "unsupported operand type(s) for -: 'str' and 'str'", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m'Hello, '\u001b[0m \u001b[0;34m-\u001b[0m \u001b[0;34m'world'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for -: 'str' and 'str'" ] } ], "source": [ "'Hello, ' - 'world'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "That stands to reason. Subtracting strings does not make sense. Python was kind enough to give us a nice error message saying that we can't have a `str` and a `str` operand type for the subtraction operation. It also makes sense that we can't do multiplication, raising of power, etc., with two strings. How about multiplying a string by an integer?" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Hello, world.Hello, world.Hello, world.'" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Hello, world.' * 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yes, this makes sense! Multiplication by an integer is the same thing as just adding multiple times, so Python concatenates the string several times.\n", "\n", "As a final note on operators with strings, watch out for this:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'42'" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'4' + '2'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The result is *not* `6`, but it is a string containing the characters `'4'` and `'2'`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Order of operations\n", "\n", "The order of operations is also as we would expect. Exponentiation comes first, followed by multiplication and division, floor division, and modulo. Next comes addition and subtraction. In order of precedence, our arithmetic operator table is\n", "\n", "|precedence|operators|\n", "|:-------:|:----------:|\n", "|1 | `**`|\n", "|2 | `*`, `/`, `//`, `%`|\n", "|3 | `+`, `-`|\n", "\n", "You can also group operations with parentheses. Operations within parentheses is are always evaluated first. Let's practice." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "17" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 + 4**2" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "3.0" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1 + 4/2" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "1**3 + 2**3 + 3**3 + 4**3" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "100" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1 + 2 + 3 + 4)**2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Interestingly, we also demonstrated that the sum of the first $n$ cubes is equal to the sum of the first $n$ integers **squared**. Fun!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variables and assignment operators\n", "\n", "So far, we have essentially just used Python as an oversized desktop calculator. We would really like to be able to think about our computational problems symbolically. We mentioned **variables** at the beginning of the tutorial, but in practice we were just using numbers and strings directly. We would like to say that a variable, `a`, represents an integer and another variable `b` represents another integer. Then, we could do things like add `a` and `b`. So, we see immediately that the variables have to have a type associated with them so the Python interpreter knows what to do when we use operators with them. A variable should also have a **value** associated with it, so the interpreter knows, e.g., what to add.\n", "\n", "In order to create, or **instantiate**, a variable, we can use an **assignment operator**. This operator is the equals sign. So, let's make variables `a` and `b` and add them." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 2\n", "b = 3\n", "a + b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Great! We get what we expect! And we still have `a` and `b`." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(2, 3)" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a, b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we might be tempted to say, \"`a` is two.\" No. `a` is not two. `a` is a variable that *has a value of 2*. A variable in Python is not just its value. A variable carries with it a type. It also has more associated with it under the hood of the interpreter that we will not get into. So, you can think about a variable as a map to an address in RAM (called a [**pointer**](https://en.wikipedia.org/wiki/Pointer_(computer_programming)) in computer-speak) that stores information, including a type and a value." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Assignment/increment operators\n", "\n", "Now, let's say we wanted to update the value of `a` by adding `4.1` to it. Python will do some magic for us." ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 2\n", " 6.1\n" ] } ], "source": [ "print(type(a), a)\n", "\n", "a = a + 4.1\n", "\n", "print(type(a), a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We see that `a` was initially an integer with a value of 2. But we added `4.1` to it, so the Python interpreter knew to change its type to a `float` and update its value.\n", "\n", "This operation of updating a value can also be accomplished with an **increment operator**." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "6.1" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 2\n", "a += 4.1\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `+=` operator told the interpreter to take the value of `a` and add `4.1` to it, changing the type of `a` in the intuitive way if need be. The other six arithmetic operators have similar constructions for the associated increment operators, `-=`, `*=`, `/=`, `//=`, `%=`, and `**=`." ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "8" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 2\n", "a **= 3\n", "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Relational operators\n", "\n", "Suppose we want to compare the values of two numbers. We may want to know if they are equal for example. The operator used to test for equality is `==`, an example of a **relational operator** (also called a **comparison operator**)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The equality relational operator\n", "\n", "Let's test out the `==` to see how it works." ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5 == 5" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5 == 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that using the operator gives either `True` or `False`. These are important keywords in Python that indicate truth. `True` and `False` have a special type, called `bool`, short for **Boolean**." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "bool" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(True)" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "bool" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The equality operator, like all relational operators in Python, also works with variables, testing for equality of their *values*. Equality of the variables themselves uses **identity operators**, described [below](#Identity-operators)." ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 4\n", "b = 5\n", "c = 4\n", "\n", "a == b" ] }, { "cell_type": "code", "execution_count": 49, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a==c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's try it out with some floats." ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5.3 == 5.3" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.1 + 3.2 == 5.3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Yikes! Python is telling us that `2.1 + 3.2` is not `5.3`. This is floating point arithmetic haunting us. Note that floating point numbers that can be exactly represented with binary numbers do not have this problem." ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "2.2 + 3.2 == 5.4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This behavior is unpredictable, so here is a rule.\n", "\n", "
\n", "\n", "
Never use the `==` operator with floats.
\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Other relational operators\n", "\n", "As you might expect, there are other relational operators. The relational operators are\n", "\n", "|English|Python|\n", "|:-------|:----------:|\n", "|is equal to | `==`|\n", "|is not equal to | `!=`|\n", "|is greater than | `>`|\n", "|is less than | `<`|\n", "|is greater than or equal to | `>=`|\n", "|is less than or equal to | `<=`|\n", "\n", "We can try some of them out!" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 < 5" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "5.7 <= 3" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'michael jordan' > 'lebron james'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Whoa. What happened on that last one? The Python interpreter has weighed in on the debate about the greater basketball player of all time. It clearly thinks Michael Jordan is better than LeBron James, but that seems kind of subjective. To understand what the interpreter is doing, we need to understand how it compares strings." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A brief aside on Unicode\n", "\n", "In Python, characters are encoded with [Unicode](https://en.wikipedia.org/wiki/Unicode). This is a standardized library of characters from many languages around the world that contains over 100,000 characters. Each character has a unique number associated with it. We can access what number is assigned to a character using Python's built-in `ord()` function." ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "97" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ord('a')" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "955" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "ord('λ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The relational operators on characters compare the values that the `ord` function returns. So, using a relational operator on `'a'` and `'b'` means you are comparing `ord('a')` and `ord('b')`. When comparing strings, the interpreter first compares the first character of each string. If they are equal, it compares the second character, and so on. So, the reason that `'michael jordan' > 'lebron james'` gives a value of `True` is because `ord('m') > ord('l')`.\n", "\n", "Note that a result of this scheme is that testing for equality of strings means that **all** characters must be equal. This is the most common use case for relational operators with strings." ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'lebron' == 'lebron james'" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'lebron' == 'LeBron'" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'LeBron James' == 'LeBron James'" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'AGTCACAGTA' == 'AGTCACAGCA'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Chaining relational operators\n", "\n", "Python allow chaining of relational operators." ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 < 6 < 6.1 < 9.3" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 < 6.1 < 6 < 9.3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is convenient do to. However, it is important not to do the following, even though it is legal." ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 < 6.1 > 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In other words, do not mix the direction of the relational operators. You could run into trouble because, in this case, `5` and `4` are never compared. An expression with different relations among all three numbers also returns `True`." ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "4 < 6.1 > 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, I issue a warning.\n", "\n", "
\n", "\n", "
Do not mix the directions of chained relational operators.
\n", "\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### The numerical values of True and False\n", "\n", "As we move to conditionals, it is important to take a moment to evaluate the numerical values of the keywords `True` and `False`. They have numerical values of `1` and `0`, respectively." ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True == 1" ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "False == 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can do arithmetic on `True` and `False`, but you will get implicit type conversion." ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "1" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True + False" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "int" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "type(True + False)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Identity operators\n", "\n", "Identity operators check to see if two variables occupy the same space in memory; i.e., they are the same object. This is different that the equality relational operator, `==`, which checks to see if two variables have the same **value**. The two identity operators are in the table below.\n", "\n", "|English|Python|\n", "|:-------|:----------:|\n", "|is the same object | **`is`**|\n", "|is not the same object | **`is not`**|\n", "\n", "The operators are pretty much the same as English! Let's see these operators in action and get at the difference between `==` and `is`. Let's use the **`is`** operator to investigate how Python stores variables in memory, starting with `float`s." ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, False)" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 5.6\n", "b = 5.6\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Even though `a` and `b` have the same value, they are stored in different places in memory. They can occupy the same place in memory if we do a `b = a` assignment." ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, True)" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 5.6\n", "b = a\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Because we assigned `b = a`, they necessarily have the same (immutable) value. So, the two variables occupy the same place in memory for efficiency." ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(False, False)" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 5.6\n", "b = a\n", "a = 6.1\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the last two examples, we see that assigning `b = a`, where `a` is a `float` in this case, means that `a` and `b` occupy the same memory. However, reassigning the value of `a` resulted in the interpreter placing `a` in a new space in memory. We can double check the values.\n", "\n", "Integers sometimes do not behave the same way, however." ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, True)" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 5\n", "b = 5\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Even though we assigned `a` and `b` separately, they occupy the same place in memory. This is because Python employs **integer caching** for all integers between `-5` and `256`. This caching does not happen for more negative or larger integers." ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 350\n", "b = 350\n", "\n", "a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, let's look at strings." ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, False)" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 'Hello, world.'\n", "b = 'Hello, world.'\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So, even though `a` and `b` have the same *value*, they do not occupy the same place in memory. If we do a `b = a` assignment, we get similar results as with `float`s." ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, True)" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 'Hello, world.'\n", "b = a\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's try string assignment again with a different string." ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(True, True)" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a = 'python'\n", "b = 'python'\n", "\n", "a == b, a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Wait a minute! If we choose a string `'python'`, it occupies the same place in memory as another variable with the same value, but that was not the case for `'Hello, world.'`. This is a result of Python also doing [**string interning**](https://en.wikipedia.org/wiki/String_interning) which allows for (sometimes much more) efficient string processing. Whether two strings occupy the same place in memory depends on what the strings are.\n", "\n", "The caching and interning might be a problem, but you generally do not need to worry about it for **immutable** variables. Being immutable means that once the variables are created, their values cannot be changed. If we do change the value the variable gets a new place in memory. All variables we've encountered so far, `int`s, `float`s, and `str`s, are immutable. We will encounter mutable data types in a moment, in which case it really *does* matter practically to you as a programmer whether or not two variables are in the same location in memory." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Logical operators\n", "\n", "**Logical operators** can be used to connect relational and identity operators. Python has three logical operators.\n", "\n", "|Logic|Python|\n", "|:-------|:----------:|\n", "|AND | `and`|\n", "|OR | `or`|\n", "|NOT | `not`|\n", "\n", "The `and` operator means that if both operands are `True`, return `True`. The `or` operator gives `True` if *either* of the operands are `True`. Finally, the `not` operator negates the logical result.\n", "\n", "That might be as clear as mud to you. It is easier to learn this, as usual, by example." ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True and True" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True and False" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True or False" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True or True" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "not False and True" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "not(False and True)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 84, "metadata": {}, "output_type": "execute_result" } ], "source": [ "not False or True" ] }, { "cell_type": "code", "execution_count": 85, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 85, "metadata": {}, "output_type": "execute_result" } ], "source": [ "not (False or True)" ] }, { "cell_type": "code", "execution_count": 86, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 86, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 == 7 or 7.6 == 9.1" ] }, { "cell_type": "code", "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 87, "metadata": {}, "output_type": "execute_result" } ], "source": [ "7 == 7 and 7.6 == 9.1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "I think these examples will help you get the hang of it. Note that it is important to specify the ordering of your operations, particularly when using the `not` operator.\n", "\n", "Note also that\n", "\n", " a < b < c\n", " \n", "is equivalent to\n", "\n", " (a < b) and (b < c)\n", "\n", "With these new types of operators in hand, we can construct a more complete table of operator precedence.\n", "\n", "|precedence|operators|\n", "|:-------|:----------:|\n", "|1 | `**`|\n", "|2 | `*`, `/`, `//`, `%`|\n", "|3 | `+`, `-`|\n", "|4 | `<`, `>`, `<=`, `>=`|\n", "|5 | `==`, `!=`|\n", "|6 | `=`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=`, `//=`|\n", "|7 | `is`, `is not`|\n", "|8 | `and`, `or`, `not`|" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Operators we left out\n", "\n", "We have left out a few operators in Python. Two that we left out are the **membership operators**, `in` and `not in`, which we will visit in a forthcoming lesson. The others we left out are **bitwise operators** and operators on **sets**, which we will not be directly covering." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Type conversion\n", "Suppose you have a variable of one type, and you want to convert it to another. For example, say you have a string, `'42'`, and you want to convert it to an integer. This would happen if you were reading information from a text file, which by definition is full of strings, and you wanted to convert some string to a number. This is done as follows." ] }, { "cell_type": "code", "execution_count": 88, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "42 \n" ] } ], "source": [ "my_str = '42'\n", "my_int = int(my_str)\n", "print(my_int, type(my_int))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversely, we can convert an `int` back to a `str`." ] }, { "cell_type": "code", "execution_count": 89, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'42'" ] }, "execution_count": 89, "metadata": {}, "output_type": "execute_result" } ], "source": [ "str(my_int)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When converting a `float` to an `int`, the interpreter does not round the result, but gives the floor." ] }, { "cell_type": "code", "execution_count": 90, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 90, "metadata": {}, "output_type": "execute_result" } ], "source": [ "int(2.9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Also consider our string concatenation warning/example from above:" ] }, { "cell_type": "code", "execution_count": 91, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "42\n", "6\n" ] } ], "source": [ "print('4' + '2')\n", "print(int('4') + int('2'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Computing environment" ] }, { "cell_type": "code", "execution_count": 92, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPython 3.7.4\n", "IPython 7.1.1\n", "\n", "jupyterlab 1.1.4\n" ] } ], "source": [ "%load_ext watermark\n", "%watermark -v -p jupyterlab" ] } ], "metadata": { "anaconda-cloud": {}, "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.7.4" } }, "nbformat": 4, "nbformat_minor": 4 }