A few reminders from the Introduction before we begin work.
Our language is Python. Want to learn Java? See yourself to the door.
You'll need an IDE (Integrated Development Environment). Which one? Try python.org for a lightweight IDE. Visual Studio Code is great. (You'll have to do a bit of work to get it Python ready.) If you want an online IDE, give trinket.io or replit.com a try. My students use CodeHS.
Your IDE will have both a code editor and a console. The console accepts individual lines of code - after a return, Python evaluates the line of code and then prints the result on the next line. The code editor is really just a text editor (likely with benefits). With it you'll write programs that will almost certainly span multiple lines; and then you run them. Program output typically appears in the console. (Note sure what all this means? Please read your IDE's documentation.)
The standard Python console prompt is >>>. You type individual Python commands after the prompt; and then, when you hit enter, the command will be executed.
At CodeHS (the IDE we use in my class), one has to do a bit of work to get the Python console. Here are the instructions:
Go to your Sandbox and create a new Python 3 project.
Add a file to that project named run.sh.
In that file, type this in: python -i main.py. This will run main.py in the Python shell. (If you wish to run a program other than main.py, use its name instead.)
Hit the Run button. main.py will execute and you'll be left with the Python console prompt.
The standard Python console prompt is >>>. (Reminder: to get the Python console prompt at replit, go to Shell and enter python after the Shell prompt.) I typed each expression after the prompts below; Python replied on the line immediately after. I typed 2 + 3. Python replied 5. I typed 2 - 3. Python replied -1. Etc. Give it a try.
>>> 2 + 3
5
>>> 2 - 3
-1
>>> 2 * 3
6
>>> 2 / 3
0.6666666666666666
>>> 2 ** 3
8
>>> 2 ** 4
16
>>> 3 ** 2
9
>>> 4 ** 2
16
>>> 2 + 3 * 4
14
>>> (2 + 3) * 4
20
>>> 2 * (3 + (4 - 5))
4
>>> 12 // 4
3
>>> 12 // 5
2
>>> 12 % 4
0
>>> 12 % 5
2
The Python console is a fine little calculator!
Python knows all the common mathematical operators. We'll begin with addition, subtraction, multiplication, division and the quotient and remainder operators. (I'll only introduce these here. We'll have to say more about some later, for some are a bit more complex that they'll see at first.)
The obvious: + is addition, - is subtraction, * is multiplication, / is division .
The non-obvious: ** is exponentiation, and // and % are the integer division and modulus operators respectively. Remember long division? a // b is the quotient and a % b the remainder; that is, a // b is the number of whole times that b goes into a, and a % b is the remainder when a is divided by b. For instance, if we divide 48 by 13, we get a quotient of 3 and a remainder of 9. So 48 // 13 is 3 and 48 % 13 is 9. These are immensely important operations. You'll be surprised. (Are you surprised, or annoyed, that the symbol % does not mean percent? Why would Python change what % means? It seems perverse. The problem is that the creators of Python, in their great wisdom, wanted to use only the symbols available on a standard keyboard. This inevitably means that certain symbols will have to be repurposed.)
Python follows the usual order of operations. Parentheses override that order. Note that unlike in mathematics, we cannot use brackets in place of parentheses. (If you were to try [2 + 3] * 4, you'll get a result that seems most strange. If you were to try [2 + 3] / 4, Python would throw a fit. Here's what it said to me: TypeError: unsupported operand type(s) for /: 'list' and 'int'. More on all this later.)
I know from experience that students often don't understand at first what // and % really do. So let's pause and discuss them for a moment. (The discussion will be mathematical, but the application to Python is immediate.)
So, as I said, a // b is the number of whole times that b goes into a. Here's another way to think about that: a // b is the largest integer k such that b * k < a. Consider 13 // 5. What is the largest integer k such that 5 * k < 13? Well, 5 * 2 = 10 and 5 * 3 = 15, but 10 < 13 and 15 > 13. So the answer is 2! 13 // 5 = 2.
But of course 25 doesn't exactly divide 13. So there's some amount left over after we divide 13 by 5, and the amount left over is 3. This is the remainder, given in Python by the % operator. So 13 % 5 = 3.
Notice this lovely little equation: 13 = (13 // 5) * 5 + 13 % 5. (Work it out if you don't see it immediately.) This is one example of a general rule: for any integers a and b, a = (a // b) * b + a % b. That is, for any a and b, to get a, take the whole number of times that b goes into a, multiply that by b, and then add the remainder left when a is divided by b.
Before we leave // and % behind (I promise they'll come up again), notice this lovely little pattern for the example of division by 5:
0 % 5 = 0, 1 % 5 = 1, 2 % 5 = 2, 3 % 5 = 3, 4 % 5 = 4, 5 % 5 = 0, . . ., 9 % 5 = 5, 10 % 5 = 0, . . .
(You see, don't you, why 0 % 5 = 0? How many whole times does 5 go into 0? 0. How much is left over when we take out those 0 5's? 0. And why is 1 % 5 = 1? 0 whole 5's go into 1, and when we take out those 0 5's, we're left with a remainder of 1.)
Notice the pattern: 0 up to 4, then back to 0 again, then up to 4, then back to 0, etc. This patterns holds for any value of b. We begin at 0, go up to b - 1, then go back to 0 and continue on as before. So, if our b is 24 (and in the problem set, you'll see that value), the remainders begin at 0, go up to 23, drop to 0 again, etc.
I don't know how much you know. But I need you to know what I'm about to say, so please forgive me if you know it already. A number n raised to the fractional power p/q (where p and q are integers) is the qth root of n raised to the pth power. So for instance, 5**(3/7) is the seventh root of 5 cubed.
This gives us an easy way to take square roots. Simply raise to the 1/2 power! So if we wanted the square root of 101, we'd do:
>>> 101 ** (1/2)
10.04987562112089
Notice that the 1/2 has to be contained in parentheses. Why? Python follows the usual order of operations, which puts exponentiation before division. So if we did
>>> 101 ** 1/2
we'd first raise 101 to the first power, which is 101, and then we'd divide that by 2. So we'd get:
50.5
That's not the square root of 101. That's 101 divided by 2.
Consider these two computations:
>>> -2 ** 2
-4
>>> (-2) ** 2
4
In the first, we squared the 2 and then negated; that's why the result is negative. In the second, we negated the 2 first to get -2 and then we squared that -2 to get 4.
Think of negation is multiplication by -1. Thus the first computation above is really -1 * 2 ** 2. Order of operations requires that we square first - that's the 2 ** 2. The result is 4, and then we take that 4 and multiply by -1 to get -4. Moreover, the second computation is really (-1 * 2) ** 2; since we do parentheses first, we get -2 squared, which is 4.
This might seem like an obvious enough point, but if it's not kept in mind, mistakes will be made. Consider for instance the mathematical expression 3x2 - 5x + 1. If we compute the value of this for x = -2, we have 3(-2)2 - 5(-2) + 1 = 3⋅4 - (-10) + 1 = 12 + 10 + 1 = 23. Now let's try to have Python do it:
>>> 3 * -2 ** 2 - 5 * -2 + 1
-1
That's not right! We should have gotten 23! What went wrong? We squared just 2, and then negated that to get -4; but we were supposed to square -2 which gives 4. Here's the fix:
>>> 3 * (-2) ** 2 - 5 * -2 + 1
23
Notice the addition of parentheses around -2.
I'll mention one other nice built-in math function. You'll use it in the problem set for the chapter. It's the round function. Let's have some examples:
>>> round(1.4)
1
>>> round(1.6)
2
>>> round(2.4)
2
>>> round(2.6)
3
So, round apparently rounds to the nearest integer.
Note that way in which the function is used. It's function name, then open parenthesis, then input, then close parenthesis. This will be the most common way in which functions are called, both functions that are built in to Python and those we will write ourselves.
I said that round rounds to the nearest integer. But you have to be careful about that. For consider:
>>> round(1.5)
2
>>> round(2.5)
2
So 1.5 rounds up, but 2.5 rounds down. Curious. So what's the rule that Python uses? If the number ends in ".5", Python rounds in such a way that the final digit is even. (You'll want to know why it does that. For an answer, Google "bankers rounding".)
You can also use the built-in round function to round to a digit after the decimal point. To do so, give the round function a second input after the number you wish to round. That second input will tell Python how many digits after the decimal point to retain.
>>> round(1.23, 1)
1.2
>>> round(1.234, 2)
1.23
>>> round(1.2345, 3)
1.234
>>> round(1.23456, 4)
1.2346
So Python knows quite a bit of basic math. But what if you need more? Like say the log function. (Don't know what that is? Google it.) Can Python do log? Yes, yes it can; and lots more too. But those are extra functions. They don't come built in.
Where are they then? In a module. What's a module? A set of definitions that extends default Python. We'll see many modules before the class is done. (Inded we'll learn how to write and use our own.) What's the name of the module with more math? The name is "math". (Brilliant name, that.) How do we get the "math" module. With the import command. Like this:
>>> import math
After this line is executed, Python will then "know" all the functions in the math module. What math functions are in the math module? Google it; the search query should be "python math module". How do you use those extra math functions? The syntax is math.<function name>(<input>). (I use the angle brackets - the < and the > - to describe a bit of code; they are not themselves used, but will be replaced by an example of that they describe.) Examples:
>>> import math
>>> math.sqrt(16)
4.0
>>> math.log(16)
2.772588722239781
>>> math.floor(16.1)
16
>>> math.ceil(16.1)
17
You should read math.sqrt as "the sqrt function contained in the math module"; and the complete line math.sqrt(16) is thus read as "the sqrt function contained in the math module applied to the number 15". So we now have a second way to take square roots!
What are math.floor and math.ceil? The idea behind them is really quite simple, and I'm confident that if you read the documentation you'll understand them immediately. Read the documentation.
No doubt you'll wonder why Python uses the dot syntax (math DOT sqrt) to access the sqrt function in the math module. My answer is two part: (i) it's clear, and (ii) it's tradition. Expect to see dots all throughout the course. I'll talk more about this later.
I know that this is quite a bit of information to take in here at the start of our study. But I did want you to know how to get these extra math functions; and I did want you do see the command that will do it.
In the Python console sessions above, the console prompt >>> precedes expressions. Below the expression is its value. This is how the console works. It reads the expression contained in a line, evaluates it, prints the value that results to the screen, and then loops back to the prompt. Read-Evaluate-Print-Loop. REPL.
How do expressions become values? Substitution. For each expression (or sub-expression) we substitute its value; and we continue on until no non-trivial substitution can be done. (In a trivial substitution, we replace an object with itself.)
Example: for 2 * 2 we substitute 4.
Another: for (2 + 3) * 4, we first substitute 5 for 2 + 3 to get 5 * 4; and then for that we substitute 20.
A third: for 2 * (3 + (4 - 5)), we first substitute -1 for 4 - 5 to get 2 * (3 + -1); that becomes 2 * 2; and finally we have 4.
When after one or many substitutions no more can be done, what remains is a literal. 2 is a literal. 2 + 2 is not.
A literal is still an expression, and like all expressions it has a value. What's the value of a literal? Itself of course.
If you type a literal into the console, you just get it back again. Like this:
>>> 2
2
We can name values if we want. Like this:
>>> a_name = 2
On the left is a name; on the right is an expression. The expression is evaluated and the name becomes the name of the value that results. Thus a_name names 2.
We can place a non-literal on the right if we want. Like this:
>>> another_name = 3 ** 4
We read from right to left. Evaluate the expression on the right, and then make the name on the left a name of the value that results. In this case, another_name becomes a name of 81.
Do be careful here. The = of Python is not the = of mathematics. The Python = is a process: evaluate the expression on the right, and then make the name on the left a name of the value that results. The = of mathematics is static. It says merely that the object named on the left and object named on the right are the same object.
So do not read Python's = as "equals". Instead read it as "is a name of", or if you like call it "the assignment operator".
(I know you're curious why we'd want to name values. Be patient! I'll explain in a moment.)
We choose what names we'll use. But we can't spell them however we like. The rules:
Names must begin with a letter (upper or lowercase) or the underscore (_).
Digits may appear in a name after its first character.
No spaces! Spaces in names are evil. They're easy to miss, and if you have many consecutive spaces, it can be hard to know how many you've got.
Names cannot be keywords. (Keywords are words reserved by Python for its own use. Examples: for, in, True, False, return, import. Google "Python keywords" for the complete list.)
Names are most certainly case sensitive. dog ≠ Dog ≠ doG.
Some examples and non-examples:
spamAndEggs is fine. It's all letters. (This is an example of so-called "camel case". See the humps?)
spam_and_eggs is fine too. (This is the second common way to make multi-word names.)
spam and eggs is bad. No spaces!
_spam_ is good. We can start with an underscore.
1spam2spam3spam4 is bad. We can't start with a digit.
spam1spamspam3 is good. We can have digits, just not at the start.
def is bad. It's a keyword.
The traditionalists will call names variables. They will say that = is the assignment symbol (or assignment token). They will say that an expression of the form name = expression assigns a value to a variable and that the variable then holds that value. I'll speak that way too sometimes. Old habits die hard.
What's the point of all this? Why give names to values? So the values won't be lost! How do we recover the value named? Simply write the name! Watch:
>>> my_age_in_secs = 50 * 365 * 24 * 60 * 60
>>> my_age_in_secs
1576800000
The right side of the first line first computed my age in seconds. It then assigned that value (1576800000) to the variable my_age_in_secs. On the second line (>>> my_age_in_secs) we asked Python to give us the value of that name, and it dutifully returned the object that the name names. So the value is remembered! Just use the name and it'll be replaced by its value in any expression that contains the name.
Remember this: the value of a variable is the value (sometimes called "object") it names. In variable-assignment talk, we say that the value of a variable is the object assigned to it.
Now you understand why we need names. We need to remember values. But honestly, if you're new to all this, you don't yet really understand just how powerful is this ability to give a name to a value. You'll see. All our code will be littered with value names.
As I said, the value of a name is the object that it names; or if you prefer, the value of a variable is the object assigned to it. Let's put this to use:
>>> my_age = 50
>>> days_in_a_year = 365
>>> secs_in_a_day = 24 * 60 * 60
>>> my_age_in_secs = my_age * days_in_a_year * secs_in_a_day
>>> my_age_in_secs
1576800000
Let's think this through.
my_age is assigned the value 50. days_in_a_year is assigned the value 365. secs_in_a_day is assigned the value 86400 (after a bit of work of course).
Read the fourth line right to left - evaluate the right and then assign the value that results to the variable on the left. To evaluate the right, first replace each variable with its value and then evaluate the expression that results. Once done, that value that results is assigned to the variable my_age_in_secs.
Thus when my_age_in secs is entered on the fifth line, the value 1576800000 is printed. For recall that the console is a Read-Evaluate-Print Loop, and the value of a variable is the object that it names.
We can change the object that a name names. Like this:
>>> a_var = 12
>>> a_var
12
>>> a_var = 12 * 12
>>> a_var
144
You might of course wonder why we'd want to do this. All will become clear in time. But know that it's common. Indeed it's nearly ubiquitous. Most programs update the values of variables.
If we wish we can use a variable to update that variable. Consider:
>>> a_var = 12
>>> a_var = a_var + 1
>>> a_var
13
Once again we see that the = of Python is not the = of mathematics. Interpreted mathematically, line 2 above is a contradiction. It says that a certain number equals that number plus 1. But this isn't mathematics. It's Python; and as Python, it's a process, not a statement. We first evaluate the right. So a_var is replaced with its current value, which 12; and a 1 is then added to that 12 with the result of 13. Finally, the 13 is made the new value of a_var.
We say in such a case that we have incremented a_var by 1. We took its old value, added 1 and made the result its new value. Lines of code like this will prove quite useful in the future. (If we instead had decreased the value of the variable, we say that it has been decremented.)
One object can have two names. (Why would you want that? Well now, that's a good question. We'll return to it later.)
>>> a_var = 12
>>> b_var = a_var
>>> a_var
12
>>> b_var
12
I'll say it again. (It's that important.) In a variable assignment statement (like the first two lines above), we first evaluate the right and then assign the object that results to the variable on the left. So on the second line above, b_var is assigned the object that's assigned to a_var, for the value of a variable is the object that it's been assigned. Thus b_var becomes an alias of a_var. Both name 12; and we say that b_var is an alias.
It's exactly the same with "Superman" and "Clark Kent". Those are two names of one man. a_var and b_var are two names of one number.
I've said a bit about expressions. I've said a bit about names. I now need to say a bit about classes. I'll only barely scratch the surface, but I cannot pass them by. Know that we'll return to them. (We'll even, at the end, learn how to make our own.)
No doubt you noticed that some of the numbers returned by Python have no decimal point and that some do. For instance, compare the numbers returned by / and //:
>>> 12/3
4.0
>>> 12//3
4
We call a number with the decimal point a float; we call one without an int. These are our first examples of classes. (Classes are often also called types. I'll use "type" and "class" interchangeably.) Every value is a member of a certain class, and (as will we find) the class of which a value is a member determines what may be done with it.
We can have Python tell us the class in which a value lies. We do this with the type function. Thus:
>>> type(4.0)
<class 'float'>
>>> type(4)
<class 'int'>
You might wonder why we need two classes of number. Why not have just one generic class that might perhaps be called real? Well, I can only begin to answer here, but begin to answer I must.
We cannot lump ints and float into a single, generic numeric class, for ints are exact and floats are approximations. Burn this into your brain. If you don't, you'll make absolutely fatal mistakes in later chapters.
You'll be puzzled by this I suspect. Students often are. They often feel that it's a defect that one of our primary numeric types - the floats - gives only approximations.
But there's a good reason for this. Many good reasons actually. One concerns numbers like pi. Pi, you will recall, is the ratio of circumference to diameter in a circle. Likely you also know that pi is an irrational number; that is, its decimal expansion is infinite, and that infinite expansion cannot be created by the repetition of some finite block of digits. Thus of course we cannot exactly represent in Python (or indeed in any language); for to do so would require that we store an infinite sequence of digits in computer memory. In Python, the best we can do is give a float approximation of pi. Here's the one in the math module:
>>> import math
>>> math.pi
3.141592653589793
The same is true of all irrationals. They can be at best approximated by a float.
But this would seem to leave open the possibility that rationals might be exactly represented as floats. But even this is often beyond Python (or indeed any language). Consider this:
>>> 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
0.9999999999999999
That's 10 of 0.1, which in mathematics is exactly 1. But, as you see, that's not what Python gives us; it gives us 0.9999999999999999 instead. I won't delve into the reason for this. (Please read this if you're curious. It has to do with the form in which numbers are stored in computer memory.) Instead all I want to do is point out the fact: the 0.1 of Python is not the 0.1 of mathematics, for the former is an approximation and the later is exact.
The lesson here is that we should never assume that a float is exactly some real or other. Assume instead that it's a best an approximation. This won't be of much importance here at the start of TL!. But later on it will become absolutely crucial.
Let me take just a moment to introduce a third class, the str (short for "string") class. A later chapter is devoted to strings, but we'll need them before then.
What's a string? A string is a sequence of characters - first this character, next this one, etc. How do we make one? We make a string with quotation marks (single or double, just be consistent). For instance, "abc" is the string that contains the characters "a", "b" and "c" is that order. So too is 'abc'.
>>> type("abc")
<class 'str'>
>>> type('abc')
<class 'str'>
We'll find in a later chapter that Python provides us with a plethora of string functions. One is of such great use that I'd like to introduce it now. We call it "concatenation"; its symbol is +. Concatenation is the stick-together function; to concatenate one string to another is to stick that one onto the end of the other. Thus concatenation is (unlike addition of floats or ints) not commutative. Order matters! Watch:
>>> 'abc' + 'def'
'abcdef'
>>> 'def' + 'abc'
'defabc'
A string, like a float or an int, is a value. Thus a string can be given a name (or as we sometimes say it can be assigned to a variable); and if it is, the value of the name is the string to which it refers. Console time:
>>> a_str = 'abc'
>>> a_str
'abc'
>>> b_str = 'def'
>>> c_str = a_str + b_str
>>> c_str
'abcdef'
>>> d_str = b_str + a_str
>>> d_str
'defabc'
As I believe I might have mentioned, assignment proceeds from right to left. Thus in c_str = a_str + b_str, we first do the concatenation on the right; and then the value that results is assigned to the variable on the left.
Let's put what we've learned to use in a few simple programs. Type each program below into your code editor and then run it. (At replit, if the program is in the file named main, it will execute when the big green Run button is clicked. If the file that contains the program isn't named main, you have to run the program from the Shell. After the shell prompt, enter python <fileName.py>. Note that the ".py" at the end is required.)
The first program is demanded by tradition.
print('Hello, World!')
We told Python to print the string "Hello, World!" to the screen. It dutifully did so.
The second program computes and then prints circle area. Note that the print function can print multiple values on a line; to do so, simply separate those values with commas. The print function below first prints a string, then a number, then a second string, then a second number.
radius = 12
PI = 3.14159
circle_area = PI * (radius ** 2)
print('radius =', radius, 'and area =', circle_area)
Note the capitalization of PI. This is common (though not required) for variable names whose value will never change - constants they're called. Note too how well (if you'll excuse a bit of self-praise) the variable names are chosen. For instance, the name radius tells you precisely what the variable does. It holds the value of the radius.
When I ran the code, I got this output in the console:
radius = 12 and area = 452.38896
Let's write one final program and then call it quits. This third program will compute tax and final price. Note that one of the lines of code begins with a hash tag - #. Anything after # is a comment. It's ignored by Python; its sole purpose is to explain the code to a human reader.
pre_tax = 12.95
tax_rate = 5.2
tip_percent = 15
# tax and tip will be divided by 100 since each is a percent
total_cost = pre_tax + pre_tax * (tax_rate / 100)
print("The pre-tax price was", round(pre_tax, 2)) # round to nearest cent
print("The total cost is", round(total_cost, 2))
# tip isn't given on tax paid
with_tip = total_cost + pre_tax * (tip_percent / 100)
print("Price with tip is", round(with_tip, 2))
Here we have three instances of the print function. The second and third print to new lines. (That can be changed. Curious how? Please go read about Python's print function.)
Here's my output:
The pre-tax price was 12.95
The total cost is 13.62
Price with tip is 15.57
So far, we've not yet seen a way for a program to interact with a user as it runs. Wouldn't it be great if Python could ask the user at some point to input a value and then use that value in some computation? Well, it can. To do, we make use of the input function. (You'll need it for the program you're asked to write for the chapter.)
I'll first show you how to use the input function in the console. Try this:
>>> answer = input("Tell me your name: ")
You should see in the console after you hit enter:
Tell me your name:
Now go to the console and type in whatever you like after the color. I typed in Franklin and then hit enter. (That is indeed my name.) The string "Franklin" then became the value of the variable answer. We can see this if we type answer into the console:
>>> answer
'Franklin'
So, what the input function does is this:
It display the string insides parentheses in the console. (You pick the string. Likely it will inform the user of the kind of input desired.)
It then waits for user input.
Once the user hits enter, the input function returns the users input; and if that input is part of a variable assignment statement (as it was above), the users input becomes the value of the variable.
It's really fairly simple. But there is a twist. That twist is that, no matter what the user types in, the input function will return a string. That's true even if you type in, say, 2. It won't be the integer 2 that's returned. It will be the string '2'. Let me prove that to you:
>>> user_input = input("Gimme input: ")
Gimme input: 2
>>> user_input
'2'
>>> type(user_input)
<class 'str'>
There it is. We have an int, not a string!
But that can definitely be a problem. What if we wanted to use the user's input as ints are used? What if, for instance, we wished to divide it by 5? If we try, we get an error:
>>> user_input / 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'str' and 'int'
What's the solution? How do we get ints (or any other non-string type) out of the input function? We do type conversion. For example, we turn the string '2' into the integer 2; and we do that with the int function. Here's an example of how it works:
>>> a_string = '2'
>>> type(a_string)
<class 'str'>
>>> an_int = int(a_string)
>>> an_int
2
>>> type(an_int)
<class 'int'>
Do you see what happened there? We gave the int function a string, and it converted that string to an int. '2' became 2.
Let me show you how to put this to work in a simple program. Here's a copy-past out of my code editor:
to_square = input("Give me an integer and I'll square it: ")
to_square = int(to_square)
print(to_square ** 2)
Here's what I saw in my console after I hit Run:
Give me an integer and I'll square it: 3
9
The first value of to_square was the string '3'. The line to_square = int(to_square) converted that string to an int and made that int the new value of to_square. (Please do remember that we read variable assignment statements right to left.) After that was done, we printed the square of to_square.
I'll end this section with a curious, and really quite beautiful, way to shorten the program above. Here it is:
to_square = int(input("Give me an integer and I'll square it: "))
print(to_square ** 2)
We went from three lines of code to two. Note that the int function is now called on the first line. How do we read that first line? From the inside out. First Python calls the input function. Its output is a string. That output is immediately fed into the int function and converted to an integer, and that integer becomes the value of to_square.
I'm sure you already written code that doesn't work. We call code that doesn't work buggy code, and we call the mistake made a bug. To debug code is to find and eliminate the mistakes. (The Wikipedia article on bugs in great. I knew only a little piece of the history.)
Don't think that identification and elimination of bugs is some small task. It's what you'll mostly do. Programmers are bug catchers!
You'll encounter bugs of two basic sorts. Let's call them stop bugs and go bugs. (This isn't the usual way to categorize bugs, but I think here at the start it's the most useful way. I'll say more in later chapters.) A stop bug will quite literally stop execution of your code; Python has encountered a command that, for one reason or another, it cannot carry out. Perhaps you've used a variable before it was defined. Perhaps you misspelled a variable. Perhaps you attempted to combine objects of different data types in ways that they cannot be combined. There are lots of possibilities here; and with each new bit of Python we learn, we'll have to contend with new stop bugs.
Stop bugs are sometimes called crash bugs. They cause the program to crash.
Fortunately for us, stop bugs are always accompanied by an explanation of why Python had to stop. These explanations - error messages they're called - are often quite helpful. READ THEM! (Don't worry if you don't understand everything Python says. You'll understand what's most important.)
Here are a few deliberate console crashes.
>>> s pam = "s pam"
File "<stdin>", line 1
s pam = "s pam"
^
SyntaxError: invalid syntax
We tried to put a space in a name. No spaces!
A second:
>>> spam + eggs
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'spam' is not defined
We can't use a variable before we define it.
A third:
>>> 3 + "three"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Whelp, guess we can't add a number to a string. (What would that mean, anyway?)
One last example of a stop bug:
>>> 3/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Did you really think that Python would know how to divide by zero?
So a stop bug stops execution. As you might have guessed, a go bug does not. The program continues execution until the very end, but (and this is a big "but") the program doesn't do what you wanted it to do. With a stop bug, Python knew how to do everything it was told to do. And it did it. But what you told it to do is not what you really wanted it to do. Bugs like this are sometimes called semantic or logic bugs. Your Python is fine, but your logic is off.
Here's a little program with a go bug. Can you find it?
# Compute the side length of a cube given its volume.
volume = 125
side_length = volume ** 1/3
print(side_length)
It runs just fine and outputs a value. But that value is wrong.
(What's wrong with the code? Python follows the usual order of operations. So in volume ** 1/3, it first raises volume to the power of 1 and then divides the result by 3. That's not what we wanted. We wanted to raise volume to the one-third power instead. We do that with volume ** (1/3). Divide first, then raise to a power.)
Have you noticed that the your code editor will sometimes place a colored squiggle under a line of code? (Replit does.) Like the red squiggle you see to the side. (If you're at replit and you don't see the red squiggle when you type in this program, that means Code Intelligence is turned off. Go to the cog on the left of the replit IDE, click and then turn on Code Intelligence.) The linter did that. The linter detects problems with your code before you run it and alerts you to them with squiggles. (What's the problem with this little two-line program? We misspelled an_int on line 2. We should have an_int = an_int + 1, not an_int = a_int + 1.)
Pay attention to the squiggles! Fix them before you run the code. In this case, if you run before you fix it, the program will crash.
If you wonder why a squiggle is there, hover over it with the cursor and a message box should pop up. If you do that for the squiggle to the side, you should see undefined name 'a_int'.
Comp sci is certainly science-like. (The name is a little hint.) But it's also art-like is many and various ways. I'll end with a few words about these two aspects.
So, you know what variables are for. They're to store away values. You also know the rules for valid variable names (can use letters, can use digits, can use the underscore, can't use anything else, can't begin with a digit, can't use a keyword). But there's more to variable choice than that. I implore you to choose variable names are both succinct and descriptive.
The reason your variables should be succinct is obvious I suppose. Succinct means you have less to type, and it means you're less likely to spell it incorrectly. The reason your variables should be descriptive is perhaps a little less obvious. So what's the reason? It's that your code should be understandable by a human reader; that is, it should be readable. I once had a student write a longish program, and he chose the variable names a, b, c, d etc. What a terrible choice! (I don't mean to insult the student. He was really quite strong. It was a beginner's mistake.) This made the code so very difficult to read.
An example should make what I mean clear. Consider these two little programs.
# Program 1
a = 12
b = 16
c = (1/3) * a * b
print(c)
Now a rewrite:
# Program 2
base_area = 12
height = 16
pyramid_volume = (1/3) * base_area * height
print("if a pyramid has base area", base_area, "and height", height, "then its volume is", pyramid_volume)
If you'd read only the first, you'd have no idea what the computation meant. The second however makes that quite clear.
Of course the first is a perfectly correct computation; it does correctly compute pyramid volume. But a human reader would find it quite mysterious. Code should not be mysterious! So practice the art of the descriptive variable choice.
My second point concerns comments. Comments serve the same purpose as descriptive variables. They make the code readable. Like this:
# the coefficients in the quadratic equation x**2 - x - 1:
a = 1
b = -1
c = -1
x = (-b + (b**2 - 4*a*c)**(1/2)) / 2 # the positive solution give by the quadratic equation
(Here the variable names a, b and c are just fine since they're the usual names for the coefficients in a quadratic equation.) The comments make quite clear what the code does. So just as I implore you to choose descriptive variable names, I also implore you to comment your code.
I'll leave with a note about the human reader. That human might be you at a future time. Don't do to yourself what I've done to myself numerous times. I worked hard and finally got the code right, but I didn't comment; and then when I came back to the code later, I had no idea how the code worked. So show a little love to your future self. Comment your code.