Python is one of the most popular programming languages in the world. This page will show you a bit about what python programs look like, and will lead you through writing some basic simple code to run on your laptop or desktop computer.
Why Python?You May Be Asking Yourself…
Many people have addressed this question, but one of the best and easiest-to-understand explanations I've come across is the following, from
Python is an incredibly efficient language: your programs will do more in fewer lines of code than many other languages would require. Python's syntax will also help you write
cleancode. Your code will be easier to read, easier to debug, and easier to extend and build upon, compared to other languages.People use Python for many purposes: to make games, build web applications, solve business problems, and develop internal tools at all kinds of interesting companies. Python is also used heavily in scientific fields, for academic research and applied work.
One of the most important reasons I continue to use Python is because of the Python community, which includes an incredibly diverse and welcoming group of people. Community is essential to programmers because programming isn't a solitary pursuit. Most of us, even the most experienced programmers, need to ask advice from others who have already solved similar problems. Having a well-connected and supportive community is critical to helping you solve problems, and the Python community is fully supportive of people who are learning Python as their first programming language or coming to Python with a background in other languages.
An old tradition which dates back to the Pleistocene Epoch holds that any time you learn a new language you must write a 'hello world' program to start with. There are TONS of languages, of course, and thus there are TONS of ways to write this program. Python makes it dead simple:
print( 'hello world' )
You can dress it up a little, using the 'newline' character which is a backslash followed by an 'n', like this:
print( '\n Hello, world!' )
Python is easy. It's easy to learn, and it's easy to install. It's also free, which is nice. Navigate your browser to https://www.python.org/downloads/ and select the appropriate link for the computer platform you have, either Windows, MacOS, Linux/UNIX, or something else. Follow the normal way you use to do any application installation for your platform. For example, on Windows, double-click the '.exe' file.
Python ships with a quasi-IDE
called IDLE which stands for Integrated Development
and Learning Environment
. It is a GUI-driven Python environment that includes a colored-syntax
editor and file handling capabilities, along with a couple of different ways to run programs. You can
find out all about it and try it out from here.
One really good resource for learning Python [or just about any programming language you can think of,
actually] is using the TIO web pages. TIO
stands for Try It
Online
and you can select the language from a ginormous list. Go to the home page, click on the
big arrow, and then select from the list to find Python 3. Once you open that page, you'll be able to
input your source code and click the run button at the top of the page next to the TIO logo to run it.
Once Python is installed on your computer [and added to your program search path, of course] you can
simply type python at the command prompt to start the interpreter. If you want to run a
program right away, change to the directory in which the source code is located and type python
<filename>.py and the program will run.
Do you know a programming language other than Python fairly well, but would like to learn Python? Join this Workshop to gain proficiency with the basics!
These notes are meant to be used in an interactive, classroom setting. The notes by themselves do not give you a sufficient explanation for each activity, so you'll need to be in class in person to get the full benefit. There is little to nothing in the way of explanation of any of the technical aspects of the language — that is left for your programming language class if you take one. Explanations come during the structured in-person lessons!
This Workshop is designed to be run under the I DO, WE DO, YOU DO model.
For each activity, the professor [I DO
] will first demo the finished product, explain the
learning objectives, and highlight the code structure. Next, the students and professor [WE DO
]
will do a code along
; students can copy-paste or type the code for each activity and make sure
they can run the code. The professor will then ask students to predict what certain small changes [or
extensions or modifications] to the code will do, then professor and students will try these changes
together. A number of language-specific questions from students can show up in this stage as well, and
that's great! Finally, several exercises to extend the activity code further are presented: the
professor can initiate one or more of these activities with the students [keeping the WE DO phase going
a little longer], but ultimately the professor should release the students into the YOU DO
phase
to complete the suggested extensions for independent practice when it makes sense to do so. If the
independent practice takes place in a classroom or lab, TAs and professors should be available, and
students, when confident, should perform peer teaching and learning as well.
By the end of this Workshop, all twelve activities, you should have a good understanding of:
This is a
programmingWorkshop, not aDeveloperWorkshop.The activities here are designed to expose you to elements of the Python programming language. They do not cover
enterpriseprogramming techniques such as organizing applications with Maven or other package tools, using frameworks such as Django, or building networked or web applications. All the activities are very small command line programs, with some minimal use of external libraries for the purposes of learning and illustration.
This Workshop assumes that you:
These simple sample programs are designed to be worked through in order. They start easy and then they increase in difficulty. We've already done the zero-eth one, but we'll re-visit it and then expand on it some to get us going.
Open your editor, and make a new file called
Save the file, then open a command or terminal window to the same directory where the file is stored, and type the command:
Congratulations! Your first Python program! |
|
Now let's make a program that calculates the Celcius temperature from a Fahrenheit temperature. This is
a really easy one to do using the formula
C = 5/9 * (F - 32)
or in Python parlance it looks like
degreesC = (((degreesF - 32) * 5) / 9).
However, there are a couple of new things here:
OK, let's see how this goes. First open a new editor file so you can type in the code. Name the file
something easy to type, like f2c.py. Copy and paste the following code
into the file:
degreesF = float( input( '\n Enter a temperature in degrees F: ' ) )
degreesC = float(((degreesF - 32) * 5 ) / 9)
print( "\n You entered ", degreesF, " degrees which is ", degreesC, " degrees C.\n" )
Save the file, then open a command or terminal window to the same directory where the file is stored, and type the command:
python f2c.py
When the program runs, you should see a prompt asking for a temperature, just like the first line is
asking. Enter the value 212
and press Enter. You should see:
Enter a temperature in degrees F: 212
You entered 212.0 degrees which is 100.0 degrees C.
HUZZAH!! It works! Now try some other known Fahrenheit temperatures like 32°F,
the freezing point of water or -40°F, the temperature at which the two scales have the same value.
Then try something screwy like abc
as the entry and see what happens.
OK, next let's do a bit of 'console art' and use some basic math to create a wave pattern of stars on the console window. There's a little bit of arithmetic here, but how we came up with the formula is not important to this sample program. If you can figure it out, excellent! But the important part of this first activity is on getting a feel for Python command line applications, not showing off your knowledge of trigonometry! Here is the code: |
|
import math
for row in range( 64 ):
waveHeight = math.fabs( math.sin( row * math.pi / 16.0 ) + 1 )
numberOfStars = int( round( 12 * waveHeight, 0 ) )
for col in range( numberOfStars ):
print( "*", end = "" )
print()
The output should look like this [note that everything above and including the RESTART:
thing is
from running the program using the IDLE GUI]:
Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\waveprinter.py
************
**************
*****************
*******************
********************
**********************
***********************
************************
************************
************************
***********************
**********************
********************
*******************
*****************
**************
************
**********
*******
*****
****
**
*
*
**
****
*****
*******
**********
************
**************
*****************
*******************
********************
**********************
***********************
************************
************************
************************
***********************
**********************
********************
*******************
*****************
**************
************
**********
*******
*****
****
**
*
*
**
****
*****
*******
**********
Quite a few things to talk about in this code [above the output, I mean]…
Experiment
with different expressions for generating lines of stars. Get creative and see what kind of cool things you can do!
There are hard-coded numbers in the program. These are often called magic numbers and we usually try not to use them that way. Instead, we define
constantsso that they are better defined in the code. The values 64 [number of rows], 12 [half the maximum number of stars per row], and 16.0 [half the number of rows per cycle] are just sitting in the middle of the code, making it look kind of mystical. Fix the code to use well-named variables for these values, so a reader can figure out more about what is going on.
Test yourself to see if you can make the program create a 'sawtooth' wave instead of a 'sinusoidal' wave.
| What are your odds of tossing a certain number of heads in a row? We can figure out that you have a 1 out of 2 chance of tossing heads, then 1 out of 4 to get two heads in a row, 1 out of 8 for three. But how about in general? Or at least for anything up to 10 tosses? Let's print the odds for the first few, both in the |
#
# A command line application that reports the odds of tossing a fair coin 1-10
# times and getting heads each time.
#
# We'll show odds both in "1 in n" format and in percentage format
message = "Odds of throwing {} heads in a row is 1 in {} ({}%)"
possibilities = 2
for tosses in range( 1, 11 ):
percentage = 100.0 / possibilities
line = message.format( tosses, possibilities, percentage )
print( line )
# Ready for the next iteration
possibilities *= 2
The output should look like this:
Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\waveprinter.py
Odds of throwing 1 heads in a row is 1 in 2 (50.0%)
Odds of throwing 2 heads in a row is 1 in 4 (25.0%)
Odds of throwing 3 heads in a row is 1 in 8 (12.5%)
Odds of throwing 4 heads in a row is 1 in 16 (6.25%)
Odds of throwing 5 heads in a row is 1 in 32 (3.125%)
Odds of throwing 6 heads in a row is 1 in 64 (1.5625%)
Odds of throwing 7 heads in a row is 1 in 128 (0.78125%)
Odds of throwing 8 heads in a row is 1 in 256 (0.390625%)
Odds of throwing 9 heads in a row is 1 in 512 (0.1953125%)
Odds of throwing 10 heads in a row is 1 in 1024 (0.09765625%)
Experiment
to make the program output up to 100 coin tosses.
Research
how to make thestr.format()function line up the number so they display in neat columns. Right-justify the integers and align the decimal points for the percents.
OK, we've seen a lot of basic 'stuff' now, so it's time to get a bit more sophisticated. Remember in the f2c.py exercise you were asked to test what happens when the user enters 'abc' instead of a number? You got an error message, right? This is the kind of situation that users hate and programmers spend a LOT of time fixing. The best way to watch out for that is with error handling. Python provides a nice facility called try … except which is very similar to other languages that have try … catch. This example illustrates it [along with plenty of other things, too…]. In this example, the program will prompt you to enter a number of eggs, then will show you how the pros count them. Let's hope the hens that laid them are cage free hens!. You shouldn't buy eggs from factory farms with caged chickens. That would be terrible. |
|
We'll also see how to define functions here!
Here's the code:
"""
An application that prompts the user for a number of eggs, and reports this
number in grosses and dozens.
"""
import math
def instructions():
print( "\n Welcome to the Egg Counter! " )
print( " If you have less than a billion eggs," )
print( " we'll help you count them, like the pros do." )
print( " You must enter the number of eggs as an integer." )
def main():
try:
eggs = int( input( "How many eggs? " ) )
except:
instructions()
return
if( eggs < 0 ):
instructions()
return
elif( eggs >= 1000000000 ):
instructions()
return
gross = eggs / 144
excessOverGross = eggs % 144
dozens = excessOverGross / 12
leftOver = excessOverGross % 12
print( "You have {} gross, {} dozen, and {} eggs.\n".format( gross, dozens, leftOver ) )
main()
The output should look like this:
Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\waveprinter.py
How many eggs? 123457
You have 857.3402777777778 gross, 4.083333333333333 dozen, and 1 eggs.
Experiment
Clean up the magic numbers. Consider defining new variables for eggs per dozen, dozens per gross, and so on.
Experiment
Agreat grossis 12 grosses (1728 items). Make the program handle this unit.
Experiment
Make the program only show units for which the count is nonzero.
Experiment
Figure out how to get the program to only show integer values for all of the calculation results.
| You're gonna need your multiplication tables. Since they don't print those out any more
[paper shortage] you better either know them 'by heart' or be able to generate your own. In
this activity we'll print a multiplication table to standard output whose size will be a
command line argument. We'll see that in Python, the command line arguments
are passed in an array to the main method of the app. The array has a special name:
As in the previous activity, this program has user input, so checking is required. That's one of the major principles of software security: validating input. In this program, we do not want non-integers, and we do not want values which are too big or too small. One thing to note is that Python doesn't have the notion of Here's the code: |
import sys
MIN_SIZE = 1
MAX_SIZE = 20
ARGUMENT_COUNT_ERROR = "Exactly one command line argument required"
ARGUMENT_FORMAT_ERROR = "Argument must be an integer"
SIZE_ERROR = str.format( "Size must be between {} and {}", MIN_SIZE, MAX_SIZE )
def printTable( size ):
for i in range( 1, (size + 1) ):
for j in range( 1, (size + 1) ):
cell = "{:4d}".format( i * j )
print( cell, end = "" )
print()
return
def main():
size = 0
try:
count = len( sys.argv )
print( "count is: ", count )
size = int( sys.argv[1] )
print( "size is: ", size )
if( count == 1 ):
print( ARGUMENT_COUNT_ERROR )
return
elif( (size < MIN_SIZE) or (size > MAX_SIZE) ):
print( SIZE_ERROR )
return
except:
print( ARGUMENT_FORMAT_ERROR )
printTable( size );
main()
The output should look like this:
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\timestables.py
Argument must be an integer
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\timestables.py 12
size is: 12
1 2 3 4 5 6 7 8 9 10 11 12
2 4 6 8 10 12 14 16 18 20 22 24
3 6 9 12 15 18 21 24 27 30 33 36
4 8 12 16 20 24 28 32 36 40 44 48
5 10 15 20 25 30 35 40 45 50 55 60
6 12 18 24 30 36 42 48 54 60 66 72
7 14 21 28 35 42 49 56 63 70 77 84
8 16 24 32 40 48 56 64 72 80 88 96
9 18 27 36 45 54 63 72 81 90 99 108
10 20 30 40 50 60 70 80 90 100 110 120
11 22 33 44 55 66 77 88 99 110 121 132
12 24 36 48 60 72 84 96 108 120 132 144
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\timestables.py aaa
Argument must be an integer
= RESTART: D:\bjohnson\PythonCode\classwork\classwork01\timestables.py 30
Size must be between 1 and 20
Experiment
See if you can change this code to output a power table rather than the times table.
Research
to see if you can find out how to 'throw' an exception when an except occurs. In other words, find out what exceptions there are, and when there is an exception, throw a NEW exception with a specific message. There is a complete hierarchical list of exceptions in Python at this link.
Python has lots of really good libraries of functionality, which are known as modules. We've seen a couple of them already. Now lets take a look at how Python handles time. The Python
Let's look at how to use these to find the day of the week on which you were born. We'll also re-visit the command line arguments again, since that is a simple way of getting input into our programs. |
|
To run this program, you enter a date as a command line argument. The date [for this example
program] should be in the form yyyy-mm-dd. Any date will work, since this
program just calculates the day of the week that date occurs.
We're going to need the two modules sys and time for this one. We'll also
use a list to hold the days of the week as strings, so we can index into it. [There are
other ways to do this using the datetime functions — those are left as an exercise for the
student.]
Here's the code:
import sys
import time
dayOfWeek = [ 'Monday', 'Tuesday', 'Wednesday', \
'Thursday' 'Friday', 'Saturday', 'Sunday' ]
if( len( sys.argv ) < 1 ):
print( "\n You must supply a date of form yyyy-mm-dd.\n" )
else:
theDate = sys.argv[1]
print( "\n Finding day of week for date:", theDate )
day_variable = time.strptime( theDate, "%Y-%m-%d" )
print( " That date's day of the week is:", dayOfWeek[day_variable.tm_wday] )
…and here's the output of a typical run:
= RESTART: D:/bjohnson/PythonCode/classwork/classwork03/dayOfWeek.py
Finding day of week for date: 1951-06-05
That date's day of the week is: Tuesday
Experiment
Try running the code with bogus dates like2022-09-37or like1900-13-00and see what happens. Then see if you can fix your code to output a message telling the user how to run the code properly.
Experiment
Modify your program to take in the year, month and day as separate arguments on the command line.
Experiment
Research how to display the input date string itself, in various text styles. Make your program respond with an entire phrase, such as, in English,August 1, 1987, was a Saturday.To get really deep into the world of dates, try to distinguish past dates from future dates in your output. Research will be required, but familiarity with the documentation is a necessary skill for programmers!
| Time to get silly and generate nonsense sentences. We are starting simply, with our program featuring only a single, fixed, story template, and a built-in set of possible words. This little program features lists and dictionaries. A For this activity, we have a dictionary with word types as keys, and the value for each key
is a list of random words. We also have a list of other random words, some of which are the
keys in the dictionary. The list acts as a |
Here's the code:
import random
words = {"noun": ["dog", "carrot", "chair", "toy", "rice cake"],
"verb": ["ran", "barked", "squeaked", "flew", "fell", "whistled"],
"adjective": ["small", "great", "fuzzy", "funny", "light"],
"preposition": ["through", "over", "under", "beyond", "across"],
"adverb": ["barely", "mostly", "easily", "already", "just"],
"color": ["pink", "blue", "mauve", "red", "transparent"] };
tokens = ["Yesterday", "the", "color", "noun",
"verb", "preposition", "the", "coach's",
"adjective", "color", "noun", "that", "was",
"adverb", "adjective", "before"];
firstWord = True
for token in tokens:
if( not firstWord ):
print( " ", end = "" )
if( token in words ):
choices = words.get( token )
print( choices[random.randint( 0, (len(choices) - 1) )], end = "" )
else:
print( token, end = "" )
firstWord = False
print( "." )
…and here's the output of a typical run, running the program seven times:
Python 3.11.1 (tags/v3.11.1:a7a450f, Dec 6 2022, 19:58:39) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license()" for more information.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the red toy barked under the coach's fuzzy transparent chair that was just funny before.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the pink chair barked beyond the coach's small pink dog that was just great before.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the blue carrot barked across the coach's great mauve rice cake that was already great before.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the blue rice cake fell through the coach's funny mauve carrot that was just light before.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the red chair flew beyond the coach's small pink toy that was already funny before.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the transparent rice cake fell across the coach's light mauve dog that was already light before.
= RESTART: D:\bjohnson\PythonCode\classwork\classwork03\nonsense.py
Yesterday the mauve chair whistled beyond the coach's funny red rice cake that was mostly funny before.
Experiment
add more words to the word list dictionarys. Add bigger words.
Experiment
Add multiple new sentence template lists. A run of the program should generate a random nonsense short story by generating five random sentences, each of which comes from a randomly selected template list.
So far we've been concentrating exclusively on fully-functioning command line apps, that we run on the command line. We've only done enough Python to make our applications work, without worrying about all the details of, and all the possible operations on, our numbers, strings, booleans, arrays, lists, and maps.
Let's shift our focus from applications, just for a short time, and use TIO to practice with some language features that are really good to know about. The session below contains a number of Python features, some of which we will talk about some during class. If something isn't mentioned in class, you're encouraged to read the documentation to learn about it. If you're still stuck, ask!
In this class we'll be doing quite a bit of work in the terminal window. That's what it's called on the Mac [or in Linux, if you are running that O/S]. On Windows it's technically called the "Command Line", but I'll just use "terminal" to cover for both so I don't have to keep saying it both ways.
So, open a terminal and type the command python to bring up the interactive python shell.
This will give you a prompt from the command line interpreter so that you can enter python code. If
you want, you can open the IDLE GUI on your computer instead, or even go to the
TIO page.
Try these out just to see if you get it...
Experiment
Can you explain the results you get from these commands?
Try these number-oriented expressions on your own. [Notice that Python distinguishes between integers and floating point values.
Experiment
Make sure you understand what happens when regular integer arithmeticoverflows, and what happens when floating-point arithmetic overflows.
Explore Python strings to see what these produce:
| Can you guess the 10 smallest countries in the world by population? Let's make this question into a quiz game! We're going to take this opportunity to introduce a new collection type: the set. We'll start with the 10 countries in a set. When you guess a country correctly, we'll remove it. If you manage to empty out the set, you will WIN. HUZZAH!! |
Here's the code:
###
# filename: GuessingGame.py
# purpose: demonstrate sets, while loops,
# break, split, and replace
# author: Dr. Johnson
# date: 2023-01-05
###
class GuessingGame:
# note that the currly braces make this a set, not a list
countries = {'vaticancity', 'nauru', 'tuvalu', \
'palau', 'sanmarino', \
'liechtenstein', 'monaco', 'st kitts', \
'marshall islands', 'dominica'}
def main( self ):
print( "\n Welcome to the Country Guessing Game. Guess the 10 smallest" )
print( " countries by population. If at any time you want to give up," )
print( " just enter the single letter 'q' at the prompt.\n" )
message = "Guess a country: "
while( len( self.countries ) != 0 ):
response = input( message )
response = self.cleanupGuess(response)
if( response == 'q' ):
break
elif( response in self.countries ):
self.countries.remove( response )
message = "Great, {} more to go! Next guess: ".format( len( self.countries ) )
else:
message = "Try again: "
if( len( self.countries ) == 0 ):
print( "\n\n YOU WIN! Wow amazing job!" )
else:
# Be nice, and show the ones still in the set
print( "\n\n That's okay, you only missed ", self.countries )
#
# We want to be liberal with what we receive from the user, trying our best to
# "normalize" it. We lowercase then remove all non-letter characters for ease
# of comparison. But there are some special cases to consider also.
def cleanupGuess( self, guess ):
guess = guess.lower()
return guess
gg = GuessingGame()
gg.main()
…and here's the output of a typical run, running the program seven times:
Welcome to the Country Guessing Game. Guess the 10 smallest
countries by population. If at any time you want to give up,
just enter the single letter 'q' at the prompt.
Guess a country: tuvalu
Great, 9 more to go! Next guess: Palau
Great, 8 more to go! Next guess: russia
Try again: SAO Tome
Try again: saotome
Try again: VATICAN CITY
Try again: VATICANCITY
Great, 7 more to go! Next guess: san marino
Try again: doctor marino
Try again: sanmarino
Great, 6 more to go! Next guess: marshall
Try again: marshall islands
Great, 5 more to go! Next guess: AnTiGuA
Try again: antigua
Try again: DoMiNiCa
Great, 4 more to go! Next guess: Monaco
Great, 3 more to go! Next guess: saint kitts
Try again: st kitts
Great, 2 more to go! Next guess: q
That's okay, you only missed {'nauru', 'liechtenstein'}
Experiment
Add more countries to the list, then modify the code to take the number of countries into account when displaying the remaining country count.
Experiment
Add a 'guess counter' into the program that will only allow the player to guess so many times before ending the game. The counter must AT LEAST allow the number of guesses that matches the number of countries in the list.
Experiment
Modify the code to base the number of allowed guesses on the number of countries in the list; add a factor to provide a few 'bad guesses' without making the player lose the game.
Experiment
Modify the program to accept different versions of the country names likeSaint Kittsforst kittsorvatican cityforvaticancity.
Experiment
Modify the program to output a message if the user enters a country that is obviously not a small population, like Russia or China or USA.
Our first few activities were all short command line apps that performed some little task, perhaps with input in the form of command line arguments and responses to prompts, then printed a result of some sort. All of these apps used entities of built-in types [numbers, booleans, strings, lists, sets], or types from a standard Python library [math, sys, random, etc.]. Now we'd like to create a type of our own, which in Python we do with the keyword class. Wait, haven't we made classes before? Didn't we create a class called GuessingGame? Well, yes, but the intent was never to make a type for new entities. It's just that, weirdly, sometimes Python requires us to make a class to house something about the program. And actually, even for the GuessingGame.py program, we could probably find a way to NOT use a class to do the same thing. Now, finally, we're going to build a REAL class whose purpose is to allow the creation of many instances of that class. So, here we go.
|
|
A die is an n-sided entity [with n >= 4] whose value is between 1 and n, inclusive. Rolling the die changes its value, but the number of sides cannot be changed.
We must provide fields in this class to reflect the number of sides on the die, as well as the current
value of the die. We must also provide functions to roll the die, get
its value, and print out what it currently looks like
[imagine a die sitting
on a table with the side that has five spots on it on top – that is what I mean by what it
looks like
.]
Here's the code:
###
# filename: Die.py
# purpose: Dice game class example
# author: Dr. Johnson
# date: 2023-01-06
###
import random
import math
class Die:
sides = 4 # default for minimum number of sides
value = 0 # default starting value
def __init__( self, numberOfSides ):
if( numberOfSides < 4 ):
raise IllegalArgumentException( " must have at least 4 sides." )
else:
self.sides = numberOfSides
def roll( self ):
self.value = random.randint( 1, self.sides )
return self.value
def getValue( self ):
return self.value
def toString( self ):
rep = "[{}]".format( self.value )
return rep
I added a bunch of test code to the bottom of the source code to instantiate two different copies of the Die class and call their functions several times, displaying the results. The testing verifies four things:
…and here's the output of a typical run:
Die 1 created ~ sides: 4
Die 1 created ~ value: 0
Die 1 rolled ~ value: 4
Die 1 rolled ~ value: 3
Die 1 rolled ~ value: 2
Die 1 rolled ~ value: 3
Die 1 rolled ~ value: 2
Die 1 rolled ~ value: 1
Die 1 getValue() says: 1
Die 1 representation : [1]
Die 2 created ~ sides: 17
Die 2 created ~ value: 0
Die 1 rolled ~ value: 17
Die 2 rolled ~ value: 8
Die 2 rolled ~ value: 3
Die 2 rolled ~ value: 8
Die 2 rolled ~ value: 14
Die 2 rolled ~ value: 1
Die 2 getValue() says: 2
Die 2 representation : [2]
Die 1 value: 1
Die 2 value: 2
Die 1 getValue() says: 1
Die 1 representation : [1]
Now, as simple example of a program that uses our Die objects, let's build a small program to roll a pair of six-sided dice thousands of times, keeping track of how often we roll a 2, or a 3, or a 4, and so on, up to a 12:
###
# filename: DiceSet.py
# purpose: Dice game class example
# author: Dr. Johnson
# date: 2023-01-06
###
import Die
class DiceSet:
myDice = []
counts = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# constructor
def __init__( self ):
d1 = Die.Die( 6 )
d2 = Die.Die( 6 )
self.myDice.append( d1 )
self.myDice.append( d2 )
# track the count
def count( self ):
index = self.myDice[0].getValue() + self.myDice[1].getValue()
self.counts[index] += 1
return
# roll all the dice one at a time
def rollAll( self ):
for i in range( 0, len(self.myDice) ):
self.myDice[i].roll()
return
def main():
# a constant to define how many rolls
MAX_ROLLS = 100000
# instantiate the dice set
dSet = DiceSet()
# roll the dice
for x in range( 1, MAX_ROLLS + 1 ):
dSet.rollAll()
dSet.count()
# print the results
print()
for x in range( 2, len(dSet.counts) ):
print( "{:2d} : {:8d}".format( x, dSet.counts[x - 1] ) )
main()
…and here's the output of a typical run:
2 : 0
3 : 2827
4 : 5548
5 : 8480
6 : 11178
7 : 13770
8 : 16630
9 : 13873
10 : 11129
11 : 8280
12 : 5466
Things to note and discuss:
Experiment
Modify the program to use three dice instead of two
Experiment
Modify the program to use a number of dice given from the command line by the user
Experiment
Modify the program to use dice with more than six sides
|
| So how do you know if your program even works? TEST IT!! Tests can increase confidence that your program is working, and the act of testing can help us find bugs before we ship our code. Many people would say that programmers even have a moral obligation to write tests. Perhaps you've already been told about, or better, have experienced, how testing is essential and can save your life [both metaphorically AND literally]. For our testing, we can use a testing framework which is a set of code in
a module that helps us set up tests for our code. The framework we'll use is called
Here is a test program for the Python |
Here's the code:
import unittest
class TestStringMethods(unittest.TestCase):
def test_upper(self):
self.assertEqual('foo'.upper(), 'FOO')
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self):
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# check that s.split fails when the separator is not a string
with self.assertRaises(TypeError):
s.split(2)
if __name__ == '__main__':
unittest.main()
THings to note here:
test_
withstatement ~ a 'better' try .. except .. finally
…and here's the output of a typical run, running the program seven times:
D:\bjohnson\PythonCode\classwork\classwork04
[Yo Beej: Need Input] <== python TestStringMethods.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
D:\bjohnson\PythonCode\classwork\classwork04
[Yo Beej: Need Input] <== python TestStringMethods.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
D:\bjohnson\PythonCode\classwork\classwork04
[Yo Beej: Need Input] <== python TestStringMethods.py
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
Experiment
add tests for the rest of the string class.
In the previous activity we saw how to write classes and create instances of classes. Classes can have constructors, fields, and methods [and a couple other things, but that is for later]. Die objects in the previous activity were mutable, which means they could
be changed; the roll method changed the state of the object it was called on by changing
the value of one of its fields. Normally in programming we strive to make objects that are
immutable. This is a matter of safety, and is the premise behind things
like encapsulation, |
|
###
# filename: Card.py
# purpose: simulation of a deck of playing cards
# author: Dr. Johnson
# date: 2023-01-07
###
class Card:
cardSuit = None
cardRank = None
def __init__( self, suit, rank ):
# thisSet = { "?", "?", "?", "?" } # can also do it with ASCII art
thisSet = { "S", "H", "D", "C" } # spades, hearts, diamonds, clubs
myRank = [0, 'A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K']
if( thisSet.isdisjoint( set( suit ) ) ):
print( " Illegal suit value sent to initializer." )
elif( 1 > rank or 13 < rank ):
print( " Illegal card rank sent to initilaizer>" )
else:
self.cardSuit = suit
self.cardRank = myRank[rank]
def toString( self ):
rep = "[{},{}]".format(str(self.cardSuit), str(self.cardRank))
return rep
# this is for testing, and will be removed later....
spades = []
for i in range( 0, 13 ):
c = Card( "S", i+1 )
spades.append( c )
print( "card: ", c.toString() )
…and here's the output of a typical run:
[Yo Beej: Need Input] <== python Card.py
card: [S,A]
card: [S,2]
card: [S,3]
card: [S,4]
card: [S,5]
card: [S,6]
card: [S,7]
card: [S,8]
card: [S,9]
card: [S,10]
card: [S,J]
card: [S,Q]
card: [S,K]
A lot of card games use a deck of all 52 cards. Let's make a class for decks. We want to be security conscious: every deck must have all 52 cards. You are allowed to shuffle the deck and ask about the positions and values of cards in the deck, but you can not add and remove cards. So what kind of a structure would be best here? Well we need a list rather than a set, because the elements are ordered.
In practice, Python applications will almost always use lists; Python arrays are much more rarely used. Arrays are generally only used in
system-levelorlow-levelcode where either performance is crucial, or you are mapping to hardware, or you are implementing the internals of some higher-level data type; for example, there are arraysunderneaththings like lists, sets, tuples, and even dictionaries, but we can't see them ~ they are hidden from view by the language. It makes things lots easier for the programmer, but on the other hand we don't get to see thenuts and boltsof the things that are going on…Arrays are really underpowered compared to lists in Python, which is why you don't even see them.
Here's the code for a deck of Card objects. We start with a normal deck of 52 cards, 13 of each of the four suits:
###
# filename: Deck.py
# purpose: simulation of a deck of playing cards
# author: Dr. Johnson
# date: 2023-01-11
###
import Card
import random
class Deck:
suits = [ "S", "H", "D", "C" ]
theDeck = []
def __init__( self ):
for s in self.suits:
for i in range( 0, 13 ):
c = Card.Card( s, i+1 )
self.theDeck.append( c )
def shuffle( self ):
for i in range( len(self.theDeck) ):
j = random.randint(0, 51)
c1 = self.theDeck[i]
c2 = self.theDeck[j]
self.theDeck[i] = c2
self.theDeck[j] = c1
def cardAt( self, index ):
try:
return self.theDeck[index]
except:
print( " Bad index in 'cardAt()' method.\n" )
def deal( self, cardCount ):
hand = []
for i in range( cardCount ):
hand.append( self.cardAt( i ).toString() )
self.theDeck.pop(i)
return hand
…and here's some code to test the Deck class:
d = Deck()
for i in range( len(d.theDeck) ):
print( d.theDeck[i].toString(), end = " " )
print()
d.shuffle()
print("\n\n AFTER SHUFFLING:" )
for i in range( len(d.theDeck) ):
print( d.theDeck[i].toString(), end = " " )
print()
myHand = []
myHand = d.deal( 5 )
print( "\n my hand: ", myHand )
print("\n\n AFTER DEALING ONE HAND:" )
for i in range( len(d.theDeck) ):
print( d.theDeck[i].toString(), end = " " )
print()
…and here's the output of a typical run [I added the line breaks]:
[Yo Beej: Need Input] <== python Deck.py
[S,A] [S,2] [S,3] [S,4] [S,5] [S,6] [S,7] [S,8] [S,9] [S,10] [S,J] [S,Q] [S,K]
[H,A] [H,2] [H,3] [H,4] [H,5] [H,6] [H,7] [H,8] [H,9] [H,10] [H,J] [H,Q] [H,K]
[D,A] [D,2] [D,3] [D,4] [D,5] [D,6] [D,7] [D,8] [D,9] [D,10] [D,J] [D,Q] [D,K]
[C,A] [C,2] [C,3] [C,4] [C,5] [C,6] [C,7] [C,8] [C,9] [C,10] [C,J] [C,Q] [C,K]
AFTER SHUFFLING:
[S,J] [H,Q] [H,8] [S,Q] [S,2] [D,9] [S,9] [S,A] [C,A] [H,10] [S,5] [S,10] [C,J]
[C,5] [C,Q] [C,2] [S,7] [D,K] [S,3] [H,6] [C,7] [D,A] [C,4] [D,2] [D,Q] [H,2]
[D,6] [S,8] [C,3] [H,K] [C,8] [S,6] [H,A] [C,9] [S,4] [S,K] [H,J] [D,3] [H,9]
[D,10] [C,10] [D,8] [D,7] [H,3] [H,4] [C,K] [D,4] [D,5] [C,6] [H,5] [D,J] [H,7]
my hand: ['[S,J]', '[H,8]', '[S,2]', '[S,9]', '[C,A]']
AFTER DEALING ONE HAND:
[H,Q] [S,Q] [D,9] [S,A] [H,10] [S,5] [S,10] [C,J] [C,5] [C,Q] [C,2] [S,7] [D,K]
[S,3] [H,6] [C,7] [D,A] [C,4] [D,2] [D,Q] [H,2] [D,6] [S,8] [C,3] [H,K] [C,8]
[S,6] [H,A] [C,9] [S,4] [S,K] [H,J] [D,3] [H,9] [D,10] [C,10] [D,8] [D,7] [H,3]
[H,4] [C,K] [D,4] [D,5] [C,6] [H,5] [D,J] [H,7]
Experiment
make a test that can deal more than one hand.
Experiment
Write a utility method that can determine from a given hand what type of poker hand you have.
Experiment
Write a program to play the gamewarby dividing the deck into two equal parts and playing one card from each half at a time such that the highest value card wins that round; in case of a tie, you should draw another card from each half to use for the comparison.
A short drive out to the country will take you to a place where there are quite a few horses, cows,
sheep, and other animals. Each of these animals has a name, and they all make their own
species-specific sounds. But when asked to speak, the all speak exactly the same way: they say their
name, then the word says
then their individual sound.
| Python captures this idea of related types that share some behaviors but differ in other
behaviors by a process called subclassing. In this operation, one Python
class describes the common attributes and activities to all the classes, then the other
|
The basic idea is that the top level class will be what's known as an abstract class. This is a class which is kind of a blueprint for other classes. It lets you create methods called abstract methods which define the methods and their calling signatures but don't actually define implementations of the methods. They are left empty and must be implemented by the subclasses.
By default, python doesn't provide a built-in mechanism for abstract classes. However, there is a
module that provides the base for defining what are called Abstract Base Classes,
the ABC module. When a function is defined with the decoration
keyword
@abstractmethod, it is an abstract method and must be implemented by the subclasses.
Here's the code. First the main class, also called the super class. We'll define
an Animal class:
###
# filename: Animal.py
# purpose: demonstrate subclassing
# author: Dr. Johnson
# date: 2023-01-15
###
from abc import ABC, abstractmethod
class Animal( ABC ): # this is a subclass of ABC
animalName = None
def __init__( self, name ):
self.animalName = name
def speak( self ):
phrase = f"\n {self.animalName}{' says '}{self.sound()}"
return phrase
@abstractmethod
def sound( self ):
pass # this indicates an empty method
Now we'll define several more unique classes for each type of animal, also called the sub classes:
###
# filename: Animal.py
# purpose: demonstrate subclassing
# author: Dr. Johnson
# date: 2023-01-15
###
from abc import ABC, abstractmethod
class Animal( ABC ): # this is a subclass of ABC
animalName = None
def __init__( self, name ):
self.animalName = name
def speak( self ):
phrase = f"\n {self.animalName}{' says '}{self.sound()}"
return phrase
@abstractmethod
def sound( self ):
pass # this indicates an empty method
class Cow( Animal ): # this is a subclass of Animal
# overriding the abstract method for Cows
def sound( self ):
return "MOO!"
class Horse( Animal ): # this is a subclass of Animal
# overriding the abstract method for Horses
def sound( self ):
return "NEIGH!"
class Goat( Animal ): # this is a subclass of Animal
# overriding the abstract method for Goats
def sound( self ):
return "MAA-AA-AA!"
class Sheep( Animal ): # this is a subclass of Animal
# overriding the abstract method for Sheep
def sound( self ):
return "BAA BAA BAA!"
## test code
groucho = Cow( "Groucho")
harpo = Horse( "Harpo")
chico = Goat( "Chico")
zeppo = Sheep( "Zeppo")
print( groucho.speak() )
print( harpo.speak() )
print( chico.speak() )
print( zeppo.speak() )
…and here's the output of a typical run:
[Yo Beej: Need Input] <== python Animal.py
Groucho says MOO!
Harpo says NEIGH!
Chico says MAA-AA-AA!
Zeppo says BAA BAA BAA!
Experiment
Add two more types of animals, meaning create two more subclasses of theAnimalsuperclass and write tests for each one.
Experiment
Break your program up into modules, with the Animal class in one file and the other animals in a second file.
Experiment
Do some research and give each of your animals a method to output its genus and species. Write tests to cover the new methods. Modify your code so the output says something like "Groucho says, 'I am a Bos Taurus and I say MOO!'