Now that you know how to use version control (Git and GitHub), please use it
consistently for all your work. This should just be how you normally work now.
Remember that it's an important "industry-relevant" skill that you should expect to use
for many years to come (your entire IT career), so make the time to get good at it.
If you do not already have your prac work with you, start by cloning your prac repository via PyCharm.
Each time you finish a practical task, commit it.
You only need to push once (when you finish and/or it's time to leave), but you can do it more often if you wish.
When you're about to make a change, like refactoring, commit first so that you can track the differences, and you can go back to a previous version if you need to.
Important
Do Not make a new project for each practical.
Keep using the same PyCharm project and GitHub repo for all practicals.
If you need to work on a new computer, you can clone your repo.
File: string_formatting.py
String formatting lets us format strings using format specifiers in a way that's very powerful and will make a lot of sense once you get used to it.
Sometimes the best way to start learning this sort of thing is to see some useful examples, so:
-
Create a new Python file called
string_formatting.py
in yourprac_03
folder. -
(Remember when copying code from GitHub to use Raw so that the formatting copies properly.)
Copy the following string formatting examples from string_formatting_examples.py into your file and run the code. (It's also written below for your reference.)
-
Use the string formatting 'mini language'; details come after the : See: https://docs.python.org/3/library/string.html#formatstrings
-
f-strings are not a complete replacement for
.format()
. There are some reasons to continue using.format()
(such as with variable unpacking) so you should know both. Many Python programmers prefer f-strings for string formatting for readability and conciseness. -
The part after the colon specifies the formatting. E.g.,
{:3}
specifies to use a width of 3 (or more if needed) for the value. -
By default, numbers are right-aligned and strings are left-aligned. You can change this with > or <
So,{:>6}
would format the value to be right-aligned and take up 6 (or more if needed) spaces.
Use f-string formatting to produce the output:
(Notice where the values go and also the float formatting / number of decimal places.)
1922 Gibson L-5 CES for about $16,036!
Using a for
loop with the range
function and f-string formatting, produce the following output.
Do not use a list and notice that both variable numbers are right-aligned:
2 ^ 0 is 1
2 ^ 1 is 2
2 ^ 2 is 4
2 ^ 3 is 8
2 ^ 4 is 16
2 ^ 5 is 32
2 ^ 6 is 64
2 ^ 7 is 128
2 ^ 8 is 256
2 ^ 9 is 512
2 ^10 is 1024
File: randoms.py
Python has a number of random functions -
contained within the random
module. Unlike the built-in functions
(print()
, input()
, etc.) the random functions are not built-in
but need to be imported. Modules are reusable collections of
functions, classes and variables (constants) related to a specific topic
(e.g., maths, operating system services, handling dates and times).
Python has a useful built-in function for finding out about the local
scope of something, called dir()
.
Launch a Python Console (in PyCharm, simply click in the
footer/status bar where it says "Python Console") and type the following
at the >>>
prompt:
dir(str)
This displays everything contained in str
, like:
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
You can see all of those useful methods such as
upper()
, startswith()
, isdecimal()
.
Next try running the dir()
function with random
as the argument:
dir(random)
The result won't be quite the same...
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name 'random' is not defined
This doesn't work, since random is a module that needs to be imported first.
Now try it like this:
import random
dir(random)
This shows all the names in the module - most of which are
functions you can use. To use functions from a module that's been
imported like this you need to qualify the name - e.g., use
random.randint
not just randint
. Use help()
to find out
about a couple of these functions:
help(random.randint)
Help on method randint in module random:
randint(a, b) method of random.Random instance
Return random integer in range [a, b], including both end points.
help(random.randrange)
Help on method randrange in module random:
randrange(start, stop=None, step=1, _int=<class 'int'>) method of random.Random instance
Choose a random item from range(start, stop[, step]).
This fixes the problem with randint() which includes the
endpoint; in Python this is usually not what you want.
The name of a function can be used without the brackets here, but this does not execute the function.
Never name a file the same as a module;
e.g., random.py
or it will have higher precedence when you import it.
In your console, type in the following (run each line multiple
times), and write the answers to the questions below in comments in randoms.py
.
import random
print(random.randint(5, 20)) # line 1
print(random.randrange(3, 10, 2)) # line 2
print(random.uniform(2.5, 5.5)) # line 3
-
What did you see on line 1?
What was the smallest number you could have seen, what was the largest? -
What did you see on line 2?
What was the smallest number you could have seen, what was the largest?
Could line 2 have produced a 4? -
What did you see on line 3?
What was the smallest number you could have seen, what was the largest? -
Write code, not a comment, to produce a random number between 1 and 100 inclusive.
File: capitalist_conrad.py
Download the code from: capitalist_conrad.py
Capitalist Conrad wants us to write a stock-price simulator for a
volatile stock. The price starts off at $10.00, and, at the end of
every day there is a 50% chance it increases by 0 to 10%, and a 50%
chance that it decreases by 0 to 5%. If the price rises above $1000, or
falls below $0.01, the program should end. The price should be
displayed to the nearest cent (e.g., $33.59
, not $33.5918232901
).
What module do we need to import? random
What functions from random do we need to use? randint
(for the 50%
chance of increase or decrease), and uniform
(to generate a random
floating-point number)
-
The program currently runs without telling us how many days it simulated.
Add this feature using anumber_of_days
variable so that the program prints like:Starting price: $10.00 On day 1 price is: $9.89 ... On day 424 price is: $915.71 On day 425 price is: $1,001.60
-
Notice how the use of
CONSTANTS
makes the program easier to read and customise.
Change these so that the allowed price range is $1 to $100 and the increase could be up to 17.5% (remember to change any comments that refer to constant values).
Run the program with these new values. -
Update your program so that it writes the output to a file.
-
First you need to
open
the file for writing. You only need to do this once, so add this line before your loop starts:out_file = open(FILENAME, 'w')
Note that this code line expects you to define the constant
FILENAME
, so do that above. -
Update any print statements, so they output to the file.
Here's an (incomplete) example:print(f"${price:,.2f}", file=out_file)
-
Close the file at the very end:
out_file.close()
-
File: exceptions_demo.py
Copy this example code that uses exceptions: exceptions_demo.py and run it, then answer the following questions in comments in your file...
- When will a
ValueError
occur? - When will a
ZeroDivisionError
occur? - Could you change the code to avoid the possibility of a
ZeroDivisionError
?
If you could figure out the answer to question 3, then make this change now.
File: exceptions_to_complete.py
Let's write a program that gets an integer from the user and does not
crash when a non-number is entered. Copy the code from
exceptions_to_complete.py
and complete the program by following the TODO
comment instructions.
Note
PyCharm will probably give you a warning that result
"may be undefined". This is safe to ignore.
It's not a PEP8 formatting warning.
It's PyCharm thinking that you might somehow exit the loop before defining result
.
Since we are controlling how we exit the loop, we know that this problem will not occur.
You're doing well. Keep it up...
File: password_checker.py
Download the starter code (complete with hints in the form of TODO
comments) from password_checker.py
Write a program that asks for and validates a person's password. The
program is not for comparing a password to a known password, but
validating the 'strength' of a new password, like you see on websites:
enter your password, and then it tells you if it's valid (matches the
required pattern) and re-prompts if it's not.
All passwords must contain at least one each of:
- digit
- lowercase letter
- uppercase letter
The starter code uses constants to store:
- the minimum and maximum length of the password
- whether a special character is required (note the Boolean naming)
- the special characters (as a string; a list is not necessary)
Remember when a program has CONSTANTS, you should use them everywhere you can so that if you change them at the top,
this change affects the whole program as expected.
E.g., if you changed the minimum length to 5, the program should print 5 and should check to make sure the password
is >= 5 characters long.
Your output should look something like this:
Please enter a valid password
Your password must be between 5 and 15 characters, and contain:
1 or more uppercase characters
1 or more lowercase characters
1 or more numbers
and 1 or more special characters: !@#$%^&*()_-=+`~,./'[]<>?{}|\
> this?
Invalid password!
> whyNot?CanIhaveThis?
Invalid password!
> 12345678901234567890aBcv@
Invalid password!
> thisISmy123Pass!
Invalid password!
> 1thisISit!
Your 10 character password is valid: 1thisISit!
Here's another run with the same code but different values for the constants (special characters are not required in this version):
Please enter a valid password
Your password must be between 2 and 6 characters, and contain:
1 or more uppercase characters
1 or more lowercase characters
1 or more numbers
> aB
Invalid password!
> HowCanIHave2Chars?
Invalid password!
> 1aB
Your 3 character password is valid: 1aB
Do not just try and Google "Python password checker" or something, but think about doing this step by step.
We have provided a suitable structure for you with TODO
comments in the code provided.
Follow these.
-
First, just check if a string has at least one lowercase character.
You can do this by looping through the string (for character in password:
) and testing each character... count the ones that match (usingcharacter.islower()
)...
At the end you know how many lowercase characters there are. -
Only when you are able to count lowercase, then, in the same loop, count the uppercase characters
That is, do not try and get all the checks working before you know that the first one works. Do one at a time. -
Then, count the numbers...
Test your code for each of these changes as you write them. -
For special characters, remember you can use the
in
operator to see if the character isin
another string (ourSPECIAL_CHARACTERS
constant). -
... keep going until you can tell how many of each kind of character there are.
-
Then put it all together and test with some different settings.
We hope that you use this incremental approach regularly.
File: files.py
The solutions for these programs are provided, to help confirm that your solution was valid.
When you execute a Python program that contains a line like
open('data.txt', 'w')
the new file "data.txt" is created in the
same folder as the Python file in your PyCharm project.
Create a new file called files.py
and do all the following separate questions in it:
The intention here is to give you experience using different ways to read files.
Make the appropriate choice of file-reading technique for each of these questions:
read()
readline()
readlines()
for line in file
-
Write code that asks the user for their name, then opens a file called
name.txt
and writes that name to it. Useopen
andclose
for this question. -
In the same file, but as if it were a separate program, write code that opens "name.txt" and reads the name (as above) then prints (note the exact output),
Hi Bob!
(or whatever the name is in the file). Do not simply print the user's input variable!
Useopen
andclose
for this question. -
Create a text file called
numbers.txt
and save it in your prac directory. Put the following three numbers on separate lines in the file and save it:
17
42
400
Write code that opensnumbers.txt
, reads only the first two numbers, adds them together then prints the result, which should be... 59. Usewith
instead ofopen
andclose
for this question. -
Now write a fourth block of code that prints the total for all lines in
numbers.txt
. This should work for a file with any number of numbers. Usewith
instead ofopen
andclose
for this question.
Remember that understanding and choosing "the right tool for the job" is a primary focus for you as a programmer.
In the questions above, you should have used read
, readline
and for line in file
for reading.
Before we're done, let's learn one more Git thing: ignoring files.
If you created your project from our template repository, then you already have this file, so there's no more to do.
The following details are here to help you understand how .gitignore
works.
If you did not use the template, then you need to do this manually now...
You have files in your project that you don't want stored in your
repo, like PyCharm metadata files. You can just choose not to add them
(as we've done until now) but they do show up as "unversioned files".
We'd prefer to only see files we should consider adding.
The solution is to use a file called .gitignore
in your repository.
Note the exact spelling, including the dot at the start and no extension.
On Unix-like systems (including Mac), the dot makes a file/folder hidden.
.gitignore
is just a plain text file that stores the names of any files or
folders you want Git not to track and not to warn you about.
Your file will still exist in your project, but not in your repository.
(If you don't already have one)
Create a file called .gitignore
in the root folder (NOT inside a subfolder) of your project,
and add it to Git.
Then enter the following line (the trailing slash means it will match a directory but not a file with that name):
.idea/
.idea
is the directory (folder) that PyCharm stores its project metadata in.
Now look at that Version Control tool window... problem solved!
Commit and Push.
If you had already committed your .idea
folder to your
repository, PyCharm does not seem to provide a way to stop tracking this.
You use the Git command line (e.g., in PyCharm's Terminal, git bash, or Mac Terminal).
Ask your tutor if you've never done this before.
Change into your project folder and run the following command:
git rm -r --cached .idea
This removes (rm
) recursively (-r
) the .idea
folder, but only from the
index, not the local disk (--cached
).
The final part of pracs will usually be for you to do outside prac time.
Use these exercises as normal practice and as ways to learn new things.
Write 3 different versions of code to generate a random Boolean (True
or False
).
Replace the literal values for the constants at the top (like
MAX_INCREASE
) with randomly generated values (within sensible ranges).
Computers use ASCII to define a character-encoding scheme for letters, digits, and other characters. It is useful to become familiar with ASCII since that is how string comparisons are made.
Mr. Black the school teacher wants an educational program for his school
students to explore the details of ASCII. He wants the app to allow a
student to input a character and see the corresponding ASCII code, and
vice versa. A sample run of the program should look like (where g
and 100
are user inputs):
Enter a character: g
The ASCII code for g is 103
Enter a number between 33 and 127: 100
The character for 100 is d
-
Start new file,
ascii_table.py
, and write code for this program. Remember that you can use the ord() and chr() functions to convert characters to ASCII integer values and vice versa. -
Add error checking so that the number entered must be between the LOWER (33) and UPPER (127) bounds. Use constants for these values and use them in both your print statement and in your while loop condition. That is, the numbers 33 and 127 should appear only once.
Use f-strings everywhere you print literals and variable values together. -
Add on to this program by writing code that displays a table with two columns, one for the numeric ASCII value and the other for the character itself. Use string formatting to align the text nicely in two columns. Print the values between LOWER and UPPER.
It should output like ("..." indicates parts that have been removed to save space):33 ! 34 " ... 99 c 100 d ...
Open your Capitalist Conrad program.
Let's step through the program using the interactive debugger now...
-
Add a breakpoint on the while line by clicking in the left margin:
-
Click the "debug" button or choose Run > Debug from the menu or press Shift+F9
The program will run until it hits the breakpoint - then stop and show you the current state of the program and its variables (both in the bottom window and in your code) -
Try all the different methods for stepping through the program using the toolbar:
Add columns to your ASCII table output from the earlier questions. Ask the user for how many columns to print, then figure out how to write loop(s) and print statements to achieve this.
The following program randomly generates words by constructing a string
from random combinations of characters. The word_format variable stores
a sequence like ccvc, which means: consonant consonant vowel consonant.
The random.choice
function is a useful way to select a single value
from a sequence of values.
Notice how the variable word
starts as an empty string and then is
constructed using repeated string concatenation (with the +
operator).
Try this and see if you can get any interesting words.
Copy the code from word_generator.py
Things To Do:
-
Get the word format from the user so that they can customise it. Convert it to lowercase using a string method.
-
Try and make the program more interesting. For example:
a. Use wildcards for the vowels (#) and consonants (%) or either (*) and make alphabetical characters use that actual character - e.g. the format "%re#l" might produce a word like "greatly" or "breuzla"
b. Automatically (randomly) generate the word_format variable.
Write a program that asks for a length and what characteristics it
must have - requirements for upper/lower/numeric/special characters -
then it should generate a password that matches.
Use your earlier program's checker functionality to validate the
generated password.
It is super important that you use any provided
solutions to help you learn, not to avoid learning!
Do the work yourself first, and only check the solutions to evaluate your work - not to do it for you. OK?
Some solutions (not all) for practicals are provided in the solutions branch of the Practicals repository on GitHub: https://github.com/CP1404/Practicals/tree/solutions
This section summarises the expectations for marking in this practical.
Please follow the submission guidelines to ensure you receive marks for your work.
- Type the URL of your GitHub practicals repository in the text box when you submit your practical.
string_formatting.py
randoms.py
capitalist_conrad.py
exceptions_demo.py
exceptions_to_complete.py
password_checker.py
files.py