<jupyter_start><jupyter_text>Important note before you begin!If in the course of completeing this tutorial there is something you don't understand or something you'd like to learn more about, you are *highly* encouraged to do some searching online to find other explanations and dig deeper.  There are an enormous number of amazing Python resources you will find by searching about basic concepts in Python. After working through this notebook, you should be able to...1. Navigate the Jupyter notebook interface and use it to     1. Write text like the text you're reading right now.    2. Write python code    3. Run python code2. Articulate the difference between three types of numbers in Python: `int`, `float`, and `complex`, and appropriately using these types of numbers to perform mathematical computations.3. Use Python to perform basic operations on numbers such as addition, subtraction, multiplication, division, and taking numbers to powers.4. Construct variables that can be assigned values.5. Use comparison operators (e.g. less than, equal to, greater than) and logical statements to control the flow of a program.6. Use "for loops" and "while loops" to code algorithms that repetitively follow and execute a sequence of instructions.7. Define your own Python functions that can be used again and again to perform specialized tasks and make code more modular. What is this interface you're currently using?You downloaded the popular [Anaconda](https://www.anaconda.com/distribution/) onto your local machine, and you have opened a [Jupyter](http://jupyter.org/) notebook, a powerful interface for working with Python and other programming languages.  We will focus in this course on using Jupyter to run Python code.The Jupyter notebook allows one to - rapidly experiment with code- write nicely typeset paragraphs using a [markup language](https://en.wikipedia.org/wiki/Markup_language) called [markdown](https://en.wikipedia.org/wiki/Markdown) and mathematics with [LaTeX](https://www.latex-project.org/)- embed images, videos, widgets, and more.Jupyter notebooks are organized into **cells**.  In fact, this text is written inside of a cell.  Each cell can contain all sorts of things:1. Python code that one wants to run.2. Text one might want to write to explain the code or anything else.3. Images or videos one might want to display.4. Tons of other cool stuff that we won't get into much in this tutorial.**Dig Deeper.** [A Gallery of interesting Jupyter notebooks](https://github.com/jupyter/jupyter/wiki/A-gallery-of-interesting-Jupyter-Notebooks).Each cell is either a coding cell, where you can write Python code, or a text cell, where you can write text in markdown and LaTeX.  To create a new coding or text cell, you can click either the "+ Code" button or the "+ Text" button at the top left of the interface.  You can also do this by hovering your cursor over the region between two cells after which you'll see buttons appear that allow the creation of a new cell at that location. Exercises - Creating new cells 1. Create a new text cell in your answer notebook.  3. Type your name in the cell and press Shift+Enter.  4. What happened when you did this?  Describe what happened by doubling clicking on the cell you just created and wrote your name in, typing out your description, and pressing Shift+Enter.  Getting help the fast way with JupyterJupyter notebooks are particularly powerful for getting help or viewing documentation in a very fast, efficient way.  For example, let's say that someone has told you there is a python command (technically a function -- we'll get to functions later) called `print` that allows one to print the output of a certain line of code, but you have no experience with print and want to find out exactly how to use it.  You can do this by typing the command "print" followed by a question mark with no spaces into a coding cell:    print? Exercises - a little practice with help in Jupyter1. Create a new coding cell in your answer notebook.1. Type `print?` into the cell, and then press Shift+Enter to run the contents of the cell.  1. The Python [**interpreter**](http://www.programiz.com/article/difference-compiler-interpreter) then reads the contents of the cell and runs whatever is inside. Create a cell below the cell where you ran the command `print?`, and describe what happened when you ran that command.  Exercise follow-upYou should see in the output that `print` is a built-in function.  This means that it's a part of the standard Python distribution.  No special packages need to be imported to use this function.  We'll be using print quite a bit while coding, especially for debugging code.  Magic FunctionsAnother powerful feature of jupyter are **magic functions**, these are commands you can type into a jupyter cell that facilitate certain actions.  Magic functions are preceded by a `%` symbol. Exercises - a little practice with magic functions1. Create a new cell in your answer notebook, type the `%` symbol, and you should see a dropdown list of magic functions available to you.  Use the arrow keys to scroll down the drop-down menu that pops up, until you've highlighted the magic function `%magic`.  Press the Enter key to tell the notebook to choose that magic function, and then run the cell containing that magic function by pressing Shift+Enter.  You should get a pop-up window at the bottom of the page explaining magic functions!  You do not need to read this whole page -- just glance through it to see what documentation looks like.  Let's dive into PythonBefore we go any further, let's engage in a time-honored computer science tradition -- writing a program that prints out the phrase "Hello World!" Exercise - the infamous "Hello World" program1. Type out        print("Hello World!")    into a new cell in your answer notebook, and press Shift+Enter to evaluate the cell.  You should see an output cell that reads `Hello World!`  Congratulations!  You have entered the world of Python programming!  Exercise follow-up: stringsIn the preceding program that printed out `Hello World!`, the text to be printed out had quotations around it.  In Python, text surrounded by quotations is called a **string**.  A string is actually a [**data type**](https://en.wikipedia.org/wiki/Data_type) in Python -- it's a way to specify and sometimes store a sequence of symbols.  Strings have many applications, but for our purposes in this tutorial, they will mainly be used to print out certain English statements at certain points in a program.  NumbersPerhaps the most fundamental thing one might want to use Python for in physical science applications is working with numbers.  What sorts of numbers can one work with in Python?  In this notebook, we will deal with three main numerical types:- **integers**    - Used to reprsent integers    - In python, integers are given the "type" `int`- **floating point numbers** aka **floats**    - Used to represent any real number to finite precision    - In python, floats are given the "type" `float`- **complex numbers** designated in Python as the type `complex`    - Used to represent complex numbers to finite precision    - In Python, complex numbers are given the "type" `complex`To type in an integer in python, you simply type out that number (in base 10 like you're used to) like this:    1377To type a float, you need to append a decimal point `.` to the end of the number so that the interpreter recognizes that you intend to type a float.      1377.    You can include zeros after the decimal point like so if you wish,    1377.0but it's not necessary.  To specify a complex number, you need to specify its real and imaginary part.  The imaginary part must be written as a float immediately followed by the character `j` with no spaces because Python uses `j` for the imaginary unit that we often write as $i$ on a piece of paper.  For example, the imaginary number $i$ would be written as follows:    1.0j    while the complex number $17+3i$ would be written as    17.0 + 3.0j     When should each type of number be used?Whether you use an `int`, `float`, or `complex` in a given application should be based on the meaning of the quantity to which the number is assigning a value.  For example, the position of a particle moving in one dimension in classical mechanics can take any value on the real line, so in this case, it is appropriate to use a `float` to represent its value when you're doing computations in Python.  On the other hand, in quantum mechanics, when one is taking linear combinations of basis vectors in the state space of a system, the coefficients in this linear combination can in general be complex numbers, so in this case it would be appropriate to use the `complex` number type for these coefficients.  Exercises - types of numbers1. In Python, there is a built-in function called `type` that can be used to check the type of a given object.  To test out this function, create a new coding cell in your answer notebook, and type the following lines        print(type(3))        print(type(3.))        print(type(3.0))        print(type(3.0 + 0.0j))    then press Shift+Enter to evaluate the cell.  Notice that in each of these cases, we are writing the number $3$, but we're just representing that number as a different numerical type in Python.2. For each of the following quantities, determine which type of number `int`, `float`, or `complex` would most appropriately be used to represent its value in a Python computation.    1. The number of particles in a system.    2. The speed of each particle in a system.    3. The number of particles per unit volume in a system.  Arithmetical operationsAny two integers, real numbers, or complex numbers can be added, subtracted, multiplied, or divided.  In python, these operations are accomplished via the operators `+`, `-`, `*`, and `/`.  When any two integers are added, subtracted, or multiplied, we know from math that one gets another integer, and python respects this mathematical fact:  Exercise - results of basic arithmetical operationsType     print(2 + 15)    print(2 - 15)    print(2 * 15)    print(type(2 + 15))    print(type(2 - 15))    print(type(2 * 15))into a new cell in your answer notebook, and press Shift+Enter to evaluate the cell.  Exercise - data type predictions1. Which data type would you predict you'd get if you were to add, subtract, multiply, or divide two floats?2. Which data type would you predict you'd get if you were to divide one integer $n$ by another integer $m$ if $n$ is not divisible by $m$?3. Which data type would you predict you'd get if you were to divide one integer $n$ by another integer $m$ if $n$ is divisible by $m$?4. In a coding cell below, do some testing to determine the answer to the questions in the last exercise for which you made predictions.  Comment on any predictions that were incorrect.  Other operationsBesides other operations, there are others one can perform on the numerical data types discussed above.  One of the most often used and useful of these is taking a number to a **power**, which in Python is denoted with a double asterisk `**`- power: `**`For example, if we wanted to compute $5^{13}$, we would type    5 ** 13Just as useful are comparison operators like equalities and inequalities:- equality: `==`- less than: `<`- greater than: `>`- less than or equal to: `<=`- greater than or equal to: `>=`- not equal to: `!=`Each of these operators takes as inputs two numbers and outputs either `True` or `False`.  For example, suppose we expect that $2^{10} > 1000$, but we want the interpreter to check this, then we could run the following code:    2 ** 10 > 1000In this case, the interpreter would output `True` because $2^{10} = 1024$ so the statement $2^{10} > 1000$ is a true statement.As we will see later, these comparison operators will be especially useful as a means of controlling the logical flow of a program.Finally, we mention the **modulo** operation denoted `%`.  This operation yields the remainder when one number is divided by another.  For example, if we were to run the code    24 % 2    Then the interpreter would output    0    because $24$ is divisible by $2$ with no remainder.  In fact, this is a convenient way to use Python to check if an integer is even -- check and see that when it's divided by two, the remainder is zero.  On the other hand, suppose we run the following code:    13 % 2    then the output in this case would be    1    because any odd integer has remainder $1$ when divided by $2$.  Exercise - more computationsPredict the results of the following computations (including the data type of the output), then try them in your notebook and print out the results in a cell below.  Do the results agree with your predictions?  If not, make sure you understand why you were in error.1. `2 ** 4`2. `2. ** 4`3. `(2 + 1j) ** 2`4. `1 == 1`5. `1. == 1`6. `1 + 0j == 1`7. `1 > 2`8. `1 >= 1.`9. `225 % 15`10. `13 % 3`  VariablesIf we want to use a programming language as more than a calculator, then we need to have the ability to assign a symbol to a value (either a number, or perhaps a value that is some other sort of object) so that we can use it repeatedly.  A symbol can be assigned a value by using a *single* equals sign `=` like so:<jupyter_code>x = 10
print(x)<jupyter_output>10<jupyter_text>In the first line, the **variable** `x` is assigned the integer value 10, and in the second line, the print command prints the value of `x`.  Notice that what the single equals sign (`=`) does is completely different than what the double equals sign (`==`) does.  The double equals sign compares two objects and outputs a **boolean** value `True` or `False` depending on whether or not the objects are the same.  The single equals sign tells the python iterpreter to associate a desired value with a specified symbol.  As you might imagine, there are rules that govern what sorts of symbols and sequences of symbols are allowed to be used as varible names in Python:- Variable names must start with either an underscore or a letter (either lowercase or upper case)- After the first symbol in a variable name, the name should conist of letters, numbers, and underscores with no spaces- Variable names are case sensitive: the variable names `variable` and `VARIABLE` are not the same  Exercise - Starting out with variablesConsider the following blocks of code.  For each code block:1. Predict what the output would be in each case including the data type where relevant.  Do not evaluate the code block in a code cell yet.2. Copy this cell over to your answer notebook.  Double click on the resulting cell where the blockes are written, and type your prediction for each block right under the block.3. For each block, create a new cell below in your answer notebook, run the code block, and see if the output agrees with your prediction.4. If the output does not agree with your prediction, create a markdown cell below the code cell where the code block was run, and explain your error. Block 1    this_is_a_variable = 13    this_is_a_variable_2 = 7    print(this_is_a_variable * this_is_a_variable_2) Block 2    variable = 10    VARIABLE = 13    print(variable == VARIABLE) Block 3    x = 10    print(x)    x = 11    print(x) Block 4    x = 10    print(x)    x = x + 3    print(x)    x = x - 3    print(x)    x = x ** 2    print(x) Block 5    x = 10    print(x)    x += 3    print(x)    x -= 3    print(x) Block 6    x = 10    print(x, type(x))    x = 13 + 0j    print(x, type(x))    x = "Goodbye Sweet World!"    print(x, type(x)) Block 7    x = 13    y = x    x = 7    print(y)    print(x)  Controlling the flow of your programsDuring the course of the program, it's common to want the Python interpreter to choose what to do next based on whether certain conditions are met.  A [**control flow**](https://en.wikipedia.org/wiki/Control_flow) statement is a statement whose execution results in a choice being made as to which of two or more paths should be followed. Using logic to control flow -- the `if` commandOne can control how a program proceeds by using **logical statements**.  For example, if you want to tell the interpreter to print the value of `y` provided the value of another variable `x` is negative, you can use an `if` statement in your code:    if x < 0:        print(y)        There are a couple of important things to note about how this statement is written.- Notice that the `if` statement on the first line ends with a colon, this is required for the interpreter to understand where the condition following the `if` ends.- The print statement on the second line is indented (by four spaces or a tab).  This is necessary for the Python interpreter to determine the **scope** of the if statement, namely which statements should be executed provided the condition following `if` is true.  This indented block for code can be of arbitrary length and therefore contain a program of arbitrary complexity.  Exercises - predicting outcomes of programs with control flow statements1. For each of the following code blocks, predict what the result would be if the block were executed.2. Copy this cell over to your answer notebook.  Create a new coding cell below it, run each block, and compare the output to your prediction.  3. If your predictions don't match the results of running each block, make sure to investigate and understand why. Block 1    x = -3    y = 2    if x < 0:        print(y) Block 2    x = -3    y = 2    if x > y:        print(x)        print(x - y)    if x < y:        print(y)        print(x + y)         Block 3    x = 2    y = -3    x = y    y = 2    if x > y:        print(y)    if x < y:        print(x)    if x == y:        print(x + y - y + x)  Exercises - writing simple programs with control flow statementsBelow are some programs described in English sentences step-by-step.  Translate each program into working python code, and then run each program in its own new coding cell below to check that it's working properly. Program 11. Assign the variables `x`, `y`, and `z` to have values `13`, `1`, and `17.1` respectively.2. If the numerical distance between `x` and `y` is less than or equal to `4`, print their sum.  Otherwise, print the value of `z`. Program 21. Assign the variables `a` and `b` to have some values you specify.2. If both `a` and `b` are even, print `a` to the power of `b`.3. If both `a` and `b` are odd, print `b` to the power of `a`.4. If `a` is even and `b` is odd, or if `a` is odd and `b` is even, print the product of `a` and `b`.  The `else` and `elif` commands The `if` command can be augmented by an `else` command that tells the interpreter what to do in the case that the statement right after `if` evaluates to `False`.  The default is that the interpreter simply ignores the indented code block after the colon, but in some cases you want to instruct the program to do something else in that case.  Suppose, for example, that you want the interpreter to take the absolute value of an `int` or `float`.  Mathematically, the absolute value is defined as $|x| = x$ *if* $x\geq 0$ and $-x$ *otherwise*.  Notice that even this mathematical definition proceeds by cases controlled by "if" and "otherwise" which in a python program would be written with an `if` and `else` command as    if x >= 0:        print(x)    else:        print(-x)Let's try this out on an example and see if it works as desired:<jupyter_code>x = -13

if x >= 0:
    print(x)
else:
    print(-x)<jupyter_output>13<jupyter_text>Notice that the `else` command worked in a similar way to the `if` command; it must be followed by a colon, and the code to be executed on the next line after the `else` should be indented by four spaces.  However, there is no separate statement after the `else` but before the colon tbecause the indented code block following `else` is executed precisely when the statement after the preceding `if` evaluates to `False`.What if you want to write a piece of code that branches in a more complicated way than just according to whether a single statement is true or false?  For example, suppose you want to implement the "signum" (aka sign) function which is defined as follows: if $x > 0$, then $\mathrm{sgn}(x) = +1$, otherwise if $x = 0$ then $\mathrm{sgn}(x) = 0$, and otherwise $\mathrm{sgn}(x) = -1$.  This sentence defining the signum function is quite easy to translate into python code as follows:    if x > 0:        print(1)    elif x == 0:        print(0)    else:        print(-1)Run the following cell to see the code in action.  Change the value of `x` a couple of times in the code to verify that it's working the way you'd expect it to.<jupyter_code>x = 0
 
if x > 0:
    print(1)
elif x == 0:
    print(0)
else:
    print(-1)<jupyter_output>0<jupyter_text>Using loops to control flow - getting the computer to repeat repeat repeat repeat ... Essentially every nontrivial, useful algorithm that one wishes to implement on a computer takes advantage of the fact that a computer can execute instructions in succession very quickly, and therefore a computer is an ideal device for performing repetitive tasks.  For example, if you wanted to manually compute the sum of all numbers from 1 to 1000 by brute force addition, starting from 1 and adding 2 then adding 3 etc., this would take quite a while.  But for a computer, especially a modern computer, this is a piece of cake.  In order to do this sort of thing, it's often appropriate to use a **loop**.  In python, there are two main kinds of loops that one might generally find useful: **for loops**, which tell python to repeat a certain code block according to a pre-specified list of steps, and **while loops** which tell python to repeat a code block as long as a certain condition evaluates to true.As an example of a simple `for` loop, let's tell the interpreter to add all of the numbers from 1 to 4:<jupyter_code>total = 0

for n in range(5):
    total += n

print(total)<jupyter_output>10<jupyter_text>Here's what happened in that cell:1. The variable `total` was assigned the integer value 0.2. The `for n in range(5)` statement tells the interpreter "consider the numbers 0, 1, ..., 5 - 1, and exectute the instructions on the code block starting on the next line for each number in that sequence"3. The code block that starts indented on the next line tells the interpreter to increase the value of the variable `total` by the amount `n` in each iteration of the loop.4. The print statement at the end is not indented, so it's not included in the loop code block.  Because of this, it is executed after the loop is finished.  Since the value of total has increased by `n` on each iteration of the loop, it's value after the loop is finished is 0 + 0 + 1 + 2 + 3 + 4 = 10, so that's the number that's printed.We can see how the loop is operating a bit more clearly if we insert some print statements in the loop code block.  Execute the code in the cell below, but before you do so, try to think to yourself what you'll see in the output when the cell is run.<jupyter_code>total = 0

for n in range(5):
    print("The current value of n is", n)
    total += n
    print("The modified value of total is", total)

print(total)<jupyter_output>The current value of n is 0
The modified value of total is 0
The current value of n is 1
The modified value of total is 1
The current value of n is 2
The modified value of total is 3
The current value of n is 3
The modified value of total is 6
The current value of n is 4
The modified value of total is 10
10<jupyter_text>The same thing can be done with a `while` loop as follows:<jupyter_code>current_number = 1
total = 0

while current_number < 5:
    total += current_number
    current_number += 1
print(total)<jupyter_output>10<jupyter_text>Can you see what changed and explain why it also gave the same answer?We said before that it should be a piece of cake for the computer to add the numbers from 1 to 1000, let's put our money where our mouth is and try it out:<jupyter_code>current_number = 1
total = 0

while current_number <= 1000:
    total += current_number
    current_number += 1
print(total)<jupyter_output>500500<jupyter_text>Wow that was fast!  Let's go up an order of magnitude and sum all numbers from 1 to 10000 and see how fast that finishes<jupyter_code>current_number = 1
total = 0

while current_number <= 10000:
    total += current_number
    current_number += 1
print(total)<jupyter_output>50005000<jupyter_text>Still extremely fast!  Ok, let's go up another three orders of magnitude<jupyter_code>current_number = 1
total = 0

while current_number <= 10000000:
    total += current_number
    current_number += 1
print(total)<jupyter_output>50000005000000<jupyter_text>All right well now the computer had to think a bit.  Let's go one higher just to make the computer try a bit harder<jupyter_code>current_number = 1
total = 0

while current_number <= 100000000:
    total += current_number
    current_number += 1
print(total)<jupyter_output>5000000050000000<jupyter_text>On my machine, that took about 20 seconds.  Exercises - predicting outcomes of programs that contain loops- For each of the code blocks below, predict what the output will be.  - After committing to a prediction and writing it down in your answer notebook, create a new cell and run each code block in its own cell.  Run the cell, and compare your prediction to the result of the program.- If you prediction doesn't agree with the result of the program, figure out why. Block 1    for n in range(10):        print(n+1)         Block 2    for n in range(6):        print(n % 2)         Block 3    for n in range(13):        if n % 2 == 0:            print(n)        else:            print("I don't like number", n) Block 4    n = 0    while n < 6:        print(n ** 2)        n += 1         Block 5    n = 40    while n > 0:        if n % 5 == 0:            print(n % 3)        n -= 4    print("What a strange program")  Exercises - writing programs that repeat using loops1. Using a while loop, print out all of the odd, positive integers less than 17.2. Using a for loop, print out all of the odd, positive integers less than 17.3. Write program that finds each even integer greater than 57 and less than 97, and each time it gets to one of these numbers, it prints out `This is a fun number!`.3. Write a program that prints out the square of every even number greater than 0 and less than 15.4. Write a program that prints out the first 10 Fibonnacci numbers.5. Write a program in which the user can specify a positive integer $n$, and the program will output the $n^\mathrm{th}$ Fibonacci number.  FunctionsWhen we want to re-use a certain piece of code, a convenient way to do so is to define a function.  We have already encountered certain functions that are built into python.  For example, we have seen that the `type` function takes some Python object (like an int or float) as its input and outputs what sort of object it is.  The `print` function takes some python expression as its input, and outputs the value of that expression.  There are lots of other functions built into Python that you can explore here, but we now want to discuss how a user can write their own Python functions. Why use functions in the first place?Among the most important motivations for writing your own python functions is the goal of writing **modular** code.  This means writing code that comes in reasonably small pieces, each of which performs a specific task.  There are any advantages to writing modular code, among the most important are:1. If code is modular, then each segment of the code can be individually developed and dubugged before being used in an entire program.  This way, if the program isn't working properly, one can be reasonably certain that the properly debugged piece of code is not the culprit.2. If code is modular, then one can separate what a certain segment of code does from the precise implementation -- the way in which it accomplishes its tasks.  This means that if at a future date, one finds a better way of achieving that task, one needs only change an isolated piece of code with the new implementation instead of writing the whole thing over again.3. Writing modular code makes it easier to plan and explain the logic of the code.  By breaking the code up into small pieces, each of which performs a specific function, one can focus on implementing each piece of code one at a time once the logic is layed out as opposed to having to think about the entire code all the time.  Also, when someone is trying to understand the code, they don't necessarily need to understand every line to get what it's doing, they'll just need to understand how all the various smaller pieces fit together.  If they want to delve into the nitty gritty implementation of each piece, they are free to do so, but they don't have to.Writing your own functions is a specific way of making code modular.  A function is an object that takes in certain inputs, does something potentially complicated to these inputs, and then returns an output.  By writing a function to perform a task, you can use this function to perform that task again and again without having to copy and paste the full code for each instance of the task. User-defined functionsIn Python, defining a function is extremely easy and intuitive.  Let's say, for example, that we want to define a function `my_square` that multiplies an `int`, `float`, or `complex` by itself, then we would write<jupyter_code>def my_square(x):
    return x * x<jupyter_output><empty_output><jupyter_text>As you can see from this example, a function definiton starts with the code `def` which tells the interpreter that the user intends to define a function.  The word `def` is then followed by a single space and then the name of the function.  Adjoined to the name of the function are a set of parenthesis that enclose the labels one wishes to use for the argument(s) of the function, and a colon follows.  After the colon, one needs to go to the next line, and indent four spaces to tell the interpreter what's in the body of the function.  The body of the function can contain any code you wish, and if you want the function to output a certain value, then that output is specified by the statement `return` followed by whatever you want the output to be.  Note that a function does not need to return anything, so one can leave the return statement blank.One nice thing about the jupyter notebook is that once you have defined a function, the notebook will recognize the function from that point on in its tab completion library, so you can only partially write our the name of the function, and the notebook will complete the name for you, or give you a list of possible completions.  Note, however, that for the function to be available for use in your program and for it to appear in the tab completion dropdown menu, the cell containing the function must first be run.  Keyword ArgumentsSuppose you want to write a python function for a line $f(x) = mx + b$, and you want the user to have the ability to specify the values of $m$ and $b$ in certain cases, but for most cases, you want the values to be set to $m = 1.0$ and $b = 2.0$ by default.  There is a way of doing this in Python using so-called **keyword arguments**.  Here's how it would work for the line example:<jupyter_code>def f(x, m = 1.0, b = 2.0):
    return m * x + b<jupyter_output><empty_output><jupyter_text>The non-optional argument $x$ is listed first, and then the optional "keyword" arguments are specified next.  The variables $m$ and $b$ are **keywords**, and default values of the keyword arguments are supplied in the form     = when the function's arguments are being specified.  Keyword arguments must always be specified after non-keyword arguments or else the Python interpreter will yell at you.  Run the coding cells below to see what happens with our newly-defined function with keyword arguments.  *Quickly think to yourself what the output will be before running each cell.*<jupyter_code>f(1)
f(3, m = 3.0)
f(2, b = 5.3)
f(5, m = 2.0, b = 7.0)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.05835560251072028, 'fraction_numerical': 0.01500838978310857, 'mean_word_length': 3.866626342053531, 'pattern_counts': {'":': 0, '<': 55, '<?xml version=': 0, '>': 58, 'https://': 7, 'lorem ipsum': 0, 'www.': 3, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '609907', 'license': 'CC0-1.0', 'n_tokens_mistral': 8521, 'n_tokens_neox': 7997, 'n_words': 5175, 'path': 'Python_Tutorial_1_Modified.ipynb', 'repo_name': 'mbabiuc/computationalphysicsmu', 'size': '32182'}