{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Operators\n", "\n", "Very generally, programming with Python - at its core - is a way to ask computer to store values and do things with them. We've now discussed variable definition and a number of the types of variables that exist in Python. But we haven't *really* started doing thins with them yet. Doing things with code requires carrying out **operations**. This is accomplished by using a number of types of **operators**. We'll introduce the different types of operators here and demonstrate how they're used when writing code." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "\n", "
\n", "Operators are special symbols in Python that carry out arithmetic or logical computation.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll discuss assignment, math, comparison and identity operators here." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Assignment Operator\n", "\n", "You're already familiar with the Python operator you'll use most often - the **assignment operator**. The aassignment operator is what you've been using for assignment - the `=`. It allows you to store information in a variable!" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python uses = for assignment.\n", "
" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# assignment operator\n", "my_var = 1" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Math Operators\n", "\n", "Beyond assignment, **math operators** will be very helpful whenever you want to carry out a calculation. At its simplest, Python is a calculator. It allows you to carry out calculations using these math operators. \n", "\n", "The first four math operators we'll introduce are `+`, `-`, `*`, and `/`, which are used for addition, subtraction, multiplication, and division, respectively. \n", "\n", "These operators return *numbers*." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python uses the mathematical operators +, -, *, / for 'sum', 'substract', 'multiply', and 'divide', repsectively.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Values can be added directly using the mathematical operators." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "12" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# addition\n", "8 + 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, math operators can also be carried out using variables that store numbers. This is likely how you'll utilize math operators more often." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "12" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# addition using variables\n", "var_a = 8 \n", "var_b = 4\n", "\n", "var_a + var_b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once defined, these variables can be used for all types of calculations, such as for subtraction, multiplation, or division:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# subtraction\n", "var_a - var_b" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "32" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# multiplcation\n", "var_a * var_b" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "2.0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# division\n", "var_a / var_b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition to the four math operators introduced, Python also has math operators for exponentiation (`**`), floor division (`//`), and returning the remainder, using what is known as the modulus (`%`)." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python also has ** for exponentiation,% for floor division (or integer division), and % for remainder (called modulus). These also return numbers.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To raise the number 2 to the power 3, carrying out the calculation that in mathematics would be denoted $2^3$, you would use the exponent operator (`**`):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# exponentiation\n", "2 ** 3" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To carry out floor division, which returns only the integer value from dividing two numbers, you would use the floor division operator (`//`):" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# floor division \n", "17 // 6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above you see that 6 goes into 17 twice. It ignores the fact that there's a remainder of 3, providing only the integer. Note that if we were to use the division operator (`/`), a float is returned and the value of 17 divided by 6 is approximately." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "2.8333333333333335" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# division\n", "17 / 6" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It's important to keep in mind that floor division does *not* round. It only returns the integer from the division, which in this case is 2.\n", "\n", "If you want the remainder of 17 divided by 6, there's an operator for that - the modulo `%`. This operator divides 17 by 6 and returns only the remainder, which is the value 5." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "5" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# remainder of 17 divided by 6\n", "17 % 6" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Order of Operations\n", "\n", "Mathematical operators follow the rules for order of operations you learned in mathematics class, learning PEMDAS or \"Please Excuse My Dear Aunt Sally\" to learn the following order of operations:\n", "\n", "- Parentheses\n", "- Exponents\n", "- Multiplication & Division\n", "- Addition & Subtraction\n", "\n", "In this first example, we see that `16 / 2` is calculated first, as it is division, followed by the addition of the value 3, ultimately returning the value '11.0' when executed. " ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "11.0" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# division before addition\n", "order_operations = 3 + 16 / 2\n", "order_operations" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Note, that if you wanted addition to occur first, you would use parentheses:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "9.5" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# use parentheses to specify addition first\n", "specify_operations = (3 + 16) / 2\n", "specify_operations" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Recap: Math Operators\n", "\n", "\n", "- `+`, `-`, `*`, `/` for addition, subtraction, multiplication, & division\n", "- `**` for exponentiation & `%` for modulus (remainder)\n", "- `//` for floor division (integer division)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Logical (Boolean) operators\n", "\n", "Logical or Boolean operators use what is known as **Boolean logic** to carry out logical operations on the computer. We'll review these rules and demonstrate how they operate in Python. \n", "\n", "The logical operators are `and`, `or`, and `not`.\n", "\n", "As mentioned previously, booleans are named after the British mathematician - George Boole. He first formulated Boolean algebra, which are a set of rules for how to reason with and combine these values. This is the basis of all modern computer logic.\n", "\n", "Unlike math operators, which return numbers, boolean operators return booleans." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python has and, or and not for boolean logic. These operators return booleans.\n", "
" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Booleans adhere to the following logic:\n", "\n", "- `and` : True if both are true\n", "- `or` : True if at least one is true\n", "- `not` : True only if false" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Following this logic, all of the following return `True`:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True and True" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True or True" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True and not False" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "not False" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# two nots cancel one another out\n", "not (not True)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Capitalization matters\n", "\n", "As discussed previously, capitalization matters in programming. Booleans are specified with only the T in True and the F in False being capitalized. If you were to specify a different capitalization, you would get a `NameError`. \n", "\n", "In addition to the error, Jupyter provides you with a visual clue. Note that in the examples above, the Booleans and operators are all bold, green font. Below, `TRUE` is neither bold nor green, cluing you into the fact that these are not booleans." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "ename": "NameError", "evalue": "name 'TRUE' is not defined", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# this will give you an error\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# 'TRUE' is not a boolean\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mTRUE\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mTRUE\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", "\u001b[0;31mNameError\u001b[0m: name 'TRUE' is not defined" ] } ], "source": [ "# this will give you an error\n", "# 'TRUE' is not a boolean\n", "TRUE and TRUE" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "### Short-circuit evaluation\n", "\n", "Before we move any further, let's discuss how Python interprets boolean operators. Python uses what is known as **short-circut evaluation**. This means that the second argument in the boolean expression is evaluated only if the first argument does not definitively determine the value of the expression. Let's look at an example:\n", "\n", "Here, Python looks at `False and` and determines that, when using `and` both sides have to be True for the expression to evaluate as True. Given that the left-hand side is False, Python *knows* that it can stop and does not even have to look at or evaluate the right-hand side. The expression thus evaluates as `False`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# python stops after False\n", "# no need to check second expression\n", "# since first is False\n", "False and print(\"Hi\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversely, if Python encounters `True` on the left-hand side, when Python encounters the `and` operator it knows it still has an opportunity for both sides to evaluate as True. It thus continues on to evaluate the right-hand side and prints \"Hi\"." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi\n" ] } ], "source": [ "# Continues to evaluate the expression\n", "True and print(\"Hi\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This leaves one final combination to discuss. Here, Python evaluates the left-hand side, printing \"Hi\" before it encounters the `and` operator. It continues on to encounter the `False` and finishes its evaluation of the code." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "scrolled": true }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi\n" ] } ], "source": [ "# evaluates print statement before and\n", "print(\"Hi\") and False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Python behaves in this way, using short-circuit evaluation, to save itself time. If it knows an expression will evaluate as False by only evaluating the left-hand side of the expression, there is no need for it to continue on to look at and use computational power to evaluate the right-hand side of the expression. Overall, this makes Python more efficient." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Comparison Operators\n", "\n", "Comparison operators compare the values on either side of the operator, returning a boolean from the comparison. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Python uses the following **comparison operators**:\n", "\n", "- `==` : values are equal\n", "- `!=` : values are not equal\n", "- `<` : value on left is less than value or right\n", "- `>` : value on left is greater than value on right\n", "- `<=` : value on left is less than *or equal to* value on right\n", "- `>=` : value on left is greater than or equal to value on the right" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python has comparison operators ==, !=, <, >, <=, and >= for value comparisons. These operators return booleans.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We mentioned previously that `=` is used for assignment rather than equality. However, there is a way to test whether two values are equal. To test equality, you'll use the `==` comparison operator. This determines whether the left-hand side of the expression is equal to the right-hand side, returning hte boolean `True` if they are equal, and `False` otherwise." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# compare equality\n", "True == True" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that you are not limited to comparing booleans with comparison operators. Here we determine whether two strings are equal." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'aa' == 'aa'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The same logic follows for the other comparison operators, which allow you to determine whether two things are *not* equal:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "True != False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and whether a value is less than or equal to another value:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "12 <= 13" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Understanding Boolean logic\n", "\n", "When first learning booleans, we sometimes *think* we know what we're asking the computer to do, but our logic can be flawed.\n", "\n", "We present these details now so that you don't fall into a trap that many beginners make early on in their journey to understand Booleans. \n", "\n", "To do so, there are three rules you'll have to understand." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "**Rule #1**: Python considers empty strings as having boolean value of `False`. Non-empty string as having boolean value of `True`.\n", "\n", "We see this play out in the examples here:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# empty string\n", "empty_string = ''\n", "bool(empty_string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above we see an empty string, created with two quotation marks, evaluates as False. We can determine how a variable will evaluate using `bool()` with the variable name of interest inside the parentheses. \n", "\n", "On the other hand, below, we see that a string with characters in it, will evaluate as `True`:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "nonempty_string = 'string has something in it'\n", "bool(nonempty_string)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "bool(None)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "**Rule #2** For the `and` operator, if left value is `True`, then right value is checked and returned. If left value is `False`, then that left value is returned.\n", "\n", "This follows with the short-circuit evaluation we've already discussed. If the left-hand side of the expression is True and Python encounters an `and`, it continues on.\n", "\n", "We've seen this before when comparing booleans:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# left value True, returns right value\n", "True and False" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But, the same logic holds when comparing strings, for example. Here we see a non-empty string (`'a'`) on the left-hand side. This evaluates as true. So, Python returns the right-hand side accordingly:" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'b'" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'a' and 'b'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Taking this a step further, we can combine logic and comparison operators. So, let's think about the following expression:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "scrolled": true, "slideshow": { "slide_type": "slide" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# the left value in parentheses is True\n", "'a' == ('b' and 'a')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you're new to programming you may see the line of code above and think it's asking if the string 'a' is equal to both 'b' and 'a'. You may *expect* this to return `False`; however, if you're following the rules we've been discussion, you'll know that this expression evaluates as `True`. Let's discuss why.\n", "\n", "Well, first things first, we know that parentheses are evaluated first, so Python first evaluates the following:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'a'" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'b' and 'a'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus, the expression above is really asking if the string 'a' is equal to the string 'a', which of course is `True`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "'a' == 'a'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Following that logic, we then know that the following evaluates as `False` because this line of code is asking whether the string `a` is equal to the string `b`, which is `False`." ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# evaluates if 'a' is equal to 'b'\n", "'a' == ('a' and 'b')" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "**Rule #3**: For the `or` operator if left value is `True`, then it is returned, otherwise if left value is `False`, then right value is returned.\n", "\n", "Again, this makes sense when we think back to short-circuit evaluation. If the left-hand side is `True` and an `or` is encountered, there is no need for Python to evaluate the right-hand side, as at least one side is true. Accordingly, Python returns the left-hand value." ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'a'" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# left-hand is True and returned\n", "'a' or 'b'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if the left hand evaluates as `False`, as is the case with an empty string, when the `or` is encountered, Python continues on and returns the right-hand side." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'b'" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# empty string evaluates as False\n", "'' or 'b'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, when return back to where this thinking started, we understand why the following evaluates as `True`." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'a' == ('a' or 'b')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we wanted to determine whether 'a' was equal to both 'a' and 'b' (a clearly False statement), we would do the following:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'a' == 'a' and 'a' == 'b'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the above example, we see that Python would evaluate the left-hand side of the expression (`'a' == 'a'`) as `True`, continuing on to the right-hand side of the `and`, evaluating the expression (`'a' == 'b'`) as False, and returning that." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Identity Operators\n", "\n", " Identity operators are used to check if two variables are located on the same part of the memory. This is what it means to **compare identity**. If two variables are stored in the same part of memory, the are idenetical." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python uses is and is not to compare identity. These operators return booleans.\n", "
" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Python uses the following **identity operators**:\n", "\n", "- `is` : True if both refer to the same object in memory\n", "- `is not` : True if they do not refer to the same object\n", "\n", "Let's see what that means with an example. Below we create three variables: `a`, `b`, and `c`. They all store the same *value* (927), but are they identical?" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "a = 927\n", "b = a\n", "c = 927" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, `a` and `b` are identical as determined here:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a is b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is because `b` is an **alias** or a copy of `a`. Python stores this alias in the same part of memory to save space. (We'll discuss aliases more later.)\n", "\n", "However, `c`, while storing the same *value* as `a` and `b` is *not* identical, as its values was defined independent of `a` and `b`" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "c is a" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ " It's important to udnerstand that two variables that are *equal* does **not** imply that they are *identical*." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Remember, if you want to test for *equality*, you would use the `==` operator:" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# testing for value equality\n", "a == b == c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Delving Deeper: Identity Operators" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "With basic understanding of identity operators under our belt, let's dig a little deeper in our understanding of how Python defines variables.\n", "\n", "We've noted before that a **new object** is created each time we have a variable that makes reference to it, but there are *few notable exceptions*:\n", "\n", "- some simple strings\n", "- Integers between -5 and 256 (inclusive)\n", "- empty immutable containers (e.g. tuples) - we'll get to these later\n", "\n", "While these may *seem* random, they exist for memory optimization in Python implementation. " ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "Shorter and less complex strings are **interned**. This means that they share the same space in memory, to maximize Python efficiency.\n", "\n", "The rules behind this are a bit fuzzy and not important for beginner-level understanding, so we'll just go through a few examples here. But, if you want to read more about string interning and how Python handles this, you can read more [here](http://guilload.com/python-string-interning/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we define two variables. Following logic from above, you may expect these strings to *not* be identical. However, these are 'simple' strings in Python world. As such, they are in fact stored in the same place of memory (interned), and by definition, identical:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# define two variables\n", "simple_string = 'string'\n", "simple_string2 = 'string'" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "simple_string is simple_string2" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To check where a variable is stored in memory, you can use the `id()` function. The variable of interest goes within the parentheses of the function. The two values returned are identical, so we know they're stored in the same place." ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4394733952 4394733952\n" ] } ], "source": [ "print(id(simple_string), id(simple_string2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, longer strings are not interned and are thus stored separately within Python's memory:" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "longer_string = 'really long string that just keeps going'\n", "longer_string2 = 'really long string that just keeps going'" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "longer_string is longer_string2" ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4433049712 4433049616\n" ] } ], "source": [ "print(id(longer_string), id(longer_string2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above, we see that these are *not* identical." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Like simple strings, integers between -5 and 256 *are* interned and thus are stored in the same place in Python's memory." ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "d = 5\n", "e = 5" ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "4391790128 4391790128\n" ] } ], "source": [ "print(id(d), id(e))" ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True\n" ] } ], "source": [ "print(d is e)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "This is because Python implementation front loads an array of integers between -5 to 256. This means these objects *already exist*." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this first example, the integer '5' and simple string 'Hello' are both interned or already available. Accordingly, `j` and `k` are identical and `l` and `m` are identical." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# Python doesn't create a new object here\n", "j = 5\n", "k = 5\n", "l = 'Hello'\n", "m = 'Hello'\n", "\n", "true_variable_integer = j is k\n", "true_variable_string = l is m\n", "\n", "print(true_variable_integer, true_variable_string)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversely, here we have an integer greater than 256 and a complex string, due to the addition of an exclamation part. Here, `n` and `o` and `p` and `q` are *not* identical. Each of these values is held separately in memory." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "# Python DOES create a new object here\n", "n = 975 #greater than 256\n", "o = 975\n", "p = 'Hello!' #that exclamation point makes it more complex\n", "q = 'Hello!'\n", "\n", "false_variable_integer = n is o\n", "false_variable_string = p is q\n", "\n", "print(false_variable_integer, false_variable_string)" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Membership Operators\n", "\n", "The final Python operator we'll introduce is the **membership operator**. These are used to check whether a value or variable is found in a sequence. These operators also return booleans." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Python uses in and not in to compare membership. These operators return booleans.\n", "
" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Here, we'll just be checking for value membership in strings. But, we'll discuss lists, tuples, sets, and dictionaries soon." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "Python uses the following membership operators:\n", "\n", "- `in` : True if value is found in the sequence\n", "- `not in` : True if value is not found in the sequence\n", "\n", "For these examples, we'll refer to the following variable (`my_string`)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [], "source": [ "my_string = 'I love Python!'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you wanted to determine whether there was an `l` in `my_string`, you would use the membership operator `in`:" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# check if l in my_string\n", "'l' in my_string" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As always, capitalization matters, so if you search for a capital L, the membership operator will return `False`:" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "scrolled": true, "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'L' in my_string" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, note that a series of characters can be tested for membership:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Python' in my_string" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## String Concatenation\n", "\n", "Now that we know all about strings and mathematics operators, it's important to note that sometimes operators behave differently on different types of variables. \n", "\n", "Above, we saw that for numbers (integers, floats, etc.), the `+` operator will add the two values together and return the sum.\n", "\n", "For strings, however, this operator works to concatenate - meaning stick together - the two strings:" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Operators sometimes do different things on different types of variables. For example, + on strings does concatenation.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we see that the `+` operator concatenages the three strings together:" ] }, { "cell_type": "code", "execution_count": 46, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "'abc'" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'a' + 'b' + 'c'" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Chaining Operators\n", "\n", "It's also possible to combine long phrases of multiple types of operators. The same logic and rules apply, and you are able to combine operators into very long and complex expression. This isn't always the *best* idea as they can be hard to intepret; however, it is programmatically possible." ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "fragment" } }, "source": [ "
\n", "Operators and variables can also be chained together into arbitrarily complex expressions.\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, this expression evaluates as `False`:" ] }, { "cell_type": "code", "execution_count": 47, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Note that you can use parentheses to chunk sections\n", "(13 % 7 >= 7) and ('Python' + ' ' + '3.7' == 'Python 3.7')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Given all we've learned so far about operators, we know that the left-hand side before the `and` is False, so the left-hand side is returned," ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "13 % 7 >= 7" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "regardless of the fact that the right-hand side of the expression is `True`:" ] }, { "cell_type": "code", "execution_count": 48, "metadata": { "slideshow": { "slide_type": "fragment" } }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'Python' + ' ' + '3.7' == 'Python 3.7'" ] }, { "cell_type": "markdown", "metadata": { "slideshow": { "slide_type": "slide" } }, "source": [ "## Exercies\n", "\n", "Q1. **What would be the value stored in `my_value` after executing the following?**\n", "\n", "A) 0.218 \n", "B) 0.875 \n", "C) 5.25 \n", "D) 40 \n", "E) Produces an error \n", "\n", "Q2. **What would be the value stored in `remainder` after executing the following?**\n", "\n", "```python\n", "remainder = 16 % 5\n", "```\n", "\n", "A) 9 \n", "B) 1 \n", "C) 3 \n", "D) 3.2 \n", "E) Produces an error\n", "\n", "Q3. **What would be the value stored in `modulo_time` after executing the following?**\n", "\n", "```python\n", "modulo_time = 4 * 2 % 5\n", "```\n", "\n", "A) 0 \n", "B) 1 \n", "C) 3 \n", "D) 3.2 \n", "E) Produces an error \n", "\n", "Q4. **What value is stored in `math_out` from the code below?**\n", "\n", "```python\n", "math_out = 32 / (1 + 3) ** 2 \n", "```\n", "\n", "Q5. **How would each of the following boolean expressions evaluate?**\n", "\n", "```python\n", "True and False\n", "True and not True or False\n", "True and not False\n", "```\n", "\n", "Q6. **Assume you're writing a videogame that will only slay the dragon if the magic lightsabre sword is charged to 90 or higher and has 100 or more energy units in its protective shield. Start with the code provided here. Replace `---` with values that will evaluate to `True` when the cell is run (and slay the dragon!).**\n", "\n", "```python\n", "sword_charge = ---\n", "shield_energy = ---\n", "\n", "(sword_charge ---) and (shield_energy ---)\n", "```\n", "\n", "Q7. **How would each of the following expressions evaluate?**\n", "\n", "```python\n", "'' and 'a'\n", "'a' == ('' and 'a')\n", "'a' == 'a' and 'a' == 'b' \n", "'a' == ('' and 'a')\n", "```\n", "\n", "Q8. **Using the variables provided, replace `---` with expression using identity operators such that `true_variable` will store `True` and `false_variable` will store `False`:\n", "\n", "```python\n", "a = 5\n", "b = 5\n", "c = b\n", "d = 'Hello!'\n", "e = 'Hello!'\n", "f = 567\n", "g = 567\n", "\n", "true_variable = ---\n", "false_variable = ---\n", "\n", "print(true_variable, false_variable)\n", "```\n", "\n", "Q9. **What would be the value stored in `my_value` after executing the following?**\n", "\n", "```python\n", "my_value = (3+2)+2/(16/2)\n", "```\n", "\n", "A) 0.218 \n", "B) 5.25 \n", "C) 20 \n", "D) 40 \n", "E) Produces an error \n", "\n", "\n", "Q10. **How would each of the following expressions evaluate?**\n", "\n", "```python\n", "17 % 7\n", "2**2 >= 4 and 13%3 > 1\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.11.8" } }, "nbformat": 4, "nbformat_minor": 4 }