Computer science is about solving problems, preferably in a beautiful and artistic way. Computers are used in every discipline from medicine (medical robotics, analysis of the spread of disease, design of drugs) to the visual arts – I’m sure you’ve seen a Pixar film recently. Computer science is one of the newest disciplines; it has completely transformed our world and will continue to do so.
Above all else, computer science is a creative discipline, mixing some of the best aspects of engineering, art, science, language, and mathematics. Learning about computation will change how you view problems in every discipline, and how you think about solutions to those problems.
Python shares many similarities with other programming languages, including Java and C++, and once you’ve learned Python, these other languages will not be difficult to pick up. Python is a fairly popular language, and it’s also fairly easy to get started with.
Let’s get started. A computer program is made up of instructions that a computer follows. Here is a simple program with one instruction:
This program causes the computer to print the word Hello
in the console window. Notice that the quotation marks around Hello
are not printed.
Here’s another program:
In this multi-line program, commands are executed in the order they appear.
As the Python interpreter runs a program, the interpreter keeps track of the line number that is currently being executed. The bookmark used to keep track of the line number is called the program counter.
The program counter always starts out pointing at the first line of the code in the program you run. It executes that line of code. Depending on what that line of code says, one of two things happens:
Why would we ever want the interpreter to suddenly jump to a new line of code? We’ll see many reasons, but the first is to write and use our own commands, called functions, that are made up of a chunk of lines of code.
By giving a name to a chunk of lines of code, we can easily re-use that section of code without having to retype all those lines every time. Here’s a program that defines our own two functions, say_hello
and why_goodbye
.
Notice that nothing is printed when you run this program. We have instructed Python that there are two functions, and described them, but have not actually instructed Python to run either of the functions.
Function definitions have two parts: a header, and a body. The header gives the name of the function you want to define (as well as some other things we’ll see later). The body is a set of lines of code that tell what the function does. Each line of the body must be indented by pressing the tab key. The indentations tell Python where the function body begins and ends.
The header uses the keyword def
(short for define), the name of the function you’d like to define, parentheses after the name of the function, and a colon. So the header for the first function is:
def say_hello():
The body of the first function is
print("Hello.")
To tell the interpreter to actually run the body of a function, you use a function call. To write a function call, write the name of the function followed by parentheses.
When the program counter reaches a function call, the interpreter takes a note of where the program counter is, moves the program counter to the first line of the body of the appropriate function definition, and executes the lines of code in the function. When the interpreter gets to the end of the body of the function, the program counter jumps back to just past the function call, and Python continues from there.
Let’s trace out the program counter’s locations as the above program executes:
say_hello()
.say_hello
is called.say_hello
, which is print("hello")
This line causes hello
to be printed.say_hello
, and so it jumps back to just past the call of say_hello
, which is the line containing the second call of say_hello
.say_hello
, which is print("hello")
This line causes hello
to be printed.say_hello
, and so it jumps back to just past the second call of say_hello
, which is the line containing the call of why_goodbye
.why_goodbye
, which is print "I don't know why you say goodbye."
This line causes I don't know why you say goodbye.
to be printed.why_goodbye
, and jumps back to the line containing the call `print(“I say hello.”)I say hello.
is printed. But this call to print
was the last line in the Python program, and so the program terminates.It is very important to understand the order in which Python executes lines of code. Stepping through code one line at a time in your mind in the order that Python executes those lines is a very good habit to get into.
Objective: Write and call your own functions to learn how the program counter steps through code.
You will write a program that introduces you and prints your favorite number.
print_favorite_number
print_favorite_number
.say_introduction
that prints Hello, my name is X.
and I am a Y.
on two separate lines, where X and Y are replaced by whatever you like.say_introduction
so that the introduction is printed before the favorite number.Test your code after steps 2 and 4: take a minute to think through what you expect to happen, and then press the run button.
Here is a solution. No peeking until you have solved it yourself!
Building higher-level concepts from simpler pieces, and then working with those higher-level concepts, is called abstraction. Abstraction is a major tool in computer science that we’ll come back to over and over.
Abstraction hides the details of how things work. Functions are our first example of abstraction. We defined a few functions, each in terms of a sequence of simpler commands. Once we defined those functions, we were able to just use them without thinking (too much) about how they worked.
Abstraction is not just important in computer science. When I press the gas pedal on my car, a lot of stuff happens, but because I have the higher level “push-gas-pedal” concept, I don’t have to think about the details of what is going on in the internal combustion engine to make my car go.
It might be fun to consider any other hobby or academic discipline you are interested in and explicitly identify the uses of abstraction, and levels of abstraction.
Here’s another big win for functions. You can use a function someone else wrote, without knowing exactly the internal details of the function. Let’s look at an example:
The first line says that this program will use the print_date_and_time
function from the simplefunctions
library of functions. (In Python, such a library is called a module.) The program then calls the print_date_and_time
function. When this function is called, the interpreter saves the current value of the program counter, sets the program counter to the beginning of the print_date_and_time function somewhere in the simplefunctions
module, executes the code it finds there, and then resets the program counter to the original, saved value.
Just like the print command, functions can take parameters – additional information that influences the results of calling the function.
When making a function call, we write the values of the parameters inside the parentheses. We say that we pass the parameters to the function at the point of call. Functions may take multiple parameters, in which case we separate them by commas when we call the function. Here’s an example:
There are a few things to notice. First, there’s the import
command. This first line says “go to the cs1lib_web
module; we’ll be using the clear
, draw_circle
, and start_graphics
functions from there.”
Then we define the draw
function. This function isn’t executed quite yet; we are just defining it. Python doesn’t even look inside the function body yet. Instead, Python jumps to the line start_graphics
.
The function call start_graphics
does two things:
start_graphics
.We never actually have to call the function draw
ourselves in this case. Instead, we passed the name of this function as a parameter to start_graphics
, and that function runs draw
. Yes, the parameter that we pass to start_graphics
is not a number, but rather a function. start_graphics
expects to be given a function as a parameter.
What do we know about start_graphics
? We know that it enables us to do graphics by opening a window on which graphics appear, and we know that it runs a function that we pass to it as a parameter. We don’t know how it does what it does, however. Abstraction at work!
Let’s look at what happens when start_graphics
gets around to calling draw
. First, draw
calls the function clear
, which clears the window.
Second, draw
calls the function draw_circle
, passing it three parameters. The first parameter, 125, says to draw the circle at x coordinate 125 in the window. The second parameter, 100, says to use the y coordinate 100. The third parameter, 50, says that the radius of the circle should be 50. The final line of the draw
function calls the draw_circle
function again, with different parameters.
It is very important to understand the order in which Python executes lines of code. Here is an exercise. Grab an index card or piece of paper, and place it under the first line of code in the program. Move the card in the same way the program counter would move. First, the import statement. Then, the line def draw
. After that, jump past the body of the draw
function, since this is just the definition of the function. Jump all the way down to start_graphics
. We know that this function calls draw
. So eventually, we jump back up to the first line of the body of draw
, and step through the lines in draw
.
Stepping through code one line at a time in the order that Python executes those lines is not very exciting, but a good habit to get into. You should step through every piece of code you see as you are learning Python.
We glossed over what the coordinates passed to the draw_circle
functions really mean. When we open a window for graphics, there’s a coordinate system within the window. Coordinates correspond to pixels in the window, with (0, 0) at the upper left corner of the window:
The coordinates (125, 100) mean the pixel that is 125 pixels to the right and 100 pixels below the upper left corner. The above diagram shows a rectangle with a black outline one pixel thick and a red fill, with its upper left corner at (3, 1) and its lower right corner at (9, 5).
Think of it this way: starting from the upper left corner of the graphics window, x coordinates increase to the right, and y coordinates increase down from the top. If you think about how x and y coordinates usually work in mathematics, graphics on a computer doesn’t quite match up. The x direction works, but in math, increasing y coordinates go up, whereas in computer graphics, increasing y coordinates go down.
What do the parameters passed to draw_circle
mean? The first two parameters give the x and y coordinates of the circle’s center, and the third coordinate gives the circle’s radius in pixels. Here’s a simple exercise for you to try: What would you see if you called draw_circle(0, 0, 100)
?
Objective: learn how to call drawing library functions that take parameters, in order to draw a desired complex picture.
Write a program that causes a yellow smiley face to be drawn on the screen. You have a 200 by 200 window available.
Draw the outline of the face. Put your code for drawing the outline after the line # draw the outline of the face
. (Lines that begin with #
are ignored by the computer, and are for human readers only.
Draw the eyes. Put your code for drawing the eyes after the line # draw the eyes
.
Bonus challenge. Draw the mouth. Hint – draw a circle, and then erase the top part of it by drawing some sort of yellow shape.
Here is a solution. No peeking until you have solved it yourself!
Computer programs are not just for computers. Good code should do two things:
A well-written program is more than a machine for solving a problem – it is in a real sense a description of a solution to a problem. How to write good, human-readable code is a major focus of this course, and we’ll come back to it again and again.
Let’s look at the following bad program.
What does this program do? Does it describe how to solve a problem? What’s the problem? Is this is a good solution? Could you modify it easily to solve a more interesting problem, without even knowing what the program does?
In this case, the program is short and you could figure out what it does by running it with Python. It would still be a lot of work to figure out what each line of code was intended to do, although we might get a high-level picture from Python.
(One sidenote: from cs1lib import *
tells Python that you want to be able to use every function from the cs1lib
module, freeing us from having to specify each and every function we intend to use.)
You can use the #
symbol in Python code to indicate code that Python should ignore. On a line of code, everything after the pound symbol is called a comment, and is ignored by Python – it’s just there for human readers.
We can now see exactly what the program is intended to do, and also which lines of code accomplish which parts of the solution.
How much should you comment your own code? Here are some guidelines:
Good comments add new information, but that does not mean you should comment every single line of code. Here’s a distracting comment that is not useful:
draw_rectangle(280, 310, 25, 40) # draw a rectangle
We can see from the function name that this line draws a rectangle. A more useful comment would tell us what we are trying to do with the rectangle (in this case, draw a door). Another comment might tell us the fill color:
# draw the door
set_fill_color(.6, .5, .3) # light brown
draw_rectangle(280, 310, 25, 40)
Organizing code into function definitions and function calls is an example of abstraction, and also is an example of good style. It’s probably not hard to guess that the function draw_rectangle
draws a rectangle. We could have drawn the rectangle by drawing four individual lines using four function calls, but it’s better style to use a single, appropriately specific function call.
In the previous example, we saw a few new graphics functions:
draw_rectangle
draws a rectangle, given four parameters. The first two give the x and y coordinates of the upper left corner of the rectangle, the third parameter gives the rectangle’s width, and the fourth parameter gives the rectangle’s height. So the call draw_rectangle(200, 250, 130, 100)
draws a rectangle with an upper left corner at (200, 250) and a lower right corner at (330, 350).
draw_triangle
takes six parameters, which it groups into three consecutive pairs. Each pair gives the coordinates of one vertex of the triangle. So the call draw_triangle(200, 250, 330, 250, 265, 200)
draws a triangle with vertices at (200, 250), (330, 250), and (265, 200).
set_fill_color
says that until told otherwise (by a subsequent call to set_fill_color
), we are drawing all filled shapes in the color specified by its three parameters. The parameters give the fraction of red, green, and blue (in that order) that, when mixed together, form the color. White would be (1, 1, 1), black would be (0, 0, 0), pure red would be (1, 0, 0), pure yellow would be (1, 1, 0) (mixing red and green), and so forth. Play with changing the parameters to set_fill_color
in the program that draws the house.
disable_stroke
just says that when we draw rectangles, circles, and triangles, draw just the inner filled part, not the outline. See what happens if you comment out the call to disable_stroke
by putting a #
in front of it.
Python ignores certain types of whitespace, such as blank lines and spaces after commas. You should use blank lines to group your code into logical segments. A good rule of thumb is that there should be a blank line roughly every 3–10 lines of code. If you have fewer blank lines, look hard to see whether you can break down your code into smaller chunks. You can think of these blank lines as serving the same purpose that new paragraphs do in English writing.
It’s also good style to put a space after every comma, just as you would in written English.
Bear in mind that whitespace at the beginning of a line is very significant. In Python, indentation indicates structure. You’ve already seen how the body of a function is indented by one tab stop. We’ll see plenty of examples of indentation as we go along.