Lab 2 Functions and Modules
The aim of this lab is to setup a simple python project using the Turtle module and experiment with writing code and functions. By the end of the Lab we should be able to
- setup a simple python project
- run programs from the command line
- expand our use of the Turtle module.
- use version control to take snapshots of our code.
We are going to create a simple folder called “TurtleFunctions” to put our code to use and test.
mkdir TurtleFunctions cd TurtleFunctions touch square.py code .
In the square.py file we will add the following
#!/usr/bin/env python import turtle def square(t, width=10): for i in range(4): t.forward(width) t.right(90) if __name__ == "__main__": my_turtle = turtle.Turtle() square(my_turtle, 100) turtle.mainloop()
__name__ and __main__
These are know as “dunder” attributes, in this particular case we are using the
__name__ one which tells programmatically if your code is being run directly as a script or if it is being imported from another module. As we will see later this can be really useful for testing and other things. For example run
python -m turtle in the terminal. This will determine that the turtle is being run as a script and run all the tests.
Getting started with git
Git is a version control system ( vcs) it can be used in a number of ways both for individual projects or working as a team. For todays example we are going to use it as an individual.
Git is a command line tool but also has a gui and it is also integrated into the IDE (VSCode).
First we need to tell git who we are and give it an email address (this helps git to know who is making the commits and who to blame!).
git config --global user.name " put your name here" git config --global user.email " put your email here"
I usually recommend using the same username as your github account same for the email address.
To get started we will change into the TurtleFunctions folder
We can run the following command to get information about the current folder.
git status fatal: not a git repository (or any of the parent directories): .git
To setup our folder as a git repository we need to run the following
git init Initialized empty Git repository in /Users/jmacey/tmp/PythonExamples/.git/
Note this will generate a .git folder which is where all the version control data will be stored.
On branch master No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) .vscode/ __init__.py __pycache__/ square.py nothing added to commit but untracked files present (use "git add" to track)
Adding some files
We are going to add some files to this folder, to start with we will create some empty files and then add them to the version control system.
touch README.md .gitignore git status On branch main No commits yet Untracked files: (use "git add <file>..." to include in what will be committed) .gitignore README.md square.py nothing added to commit but untracked files present (use "git add" to track)
To add these files to version control we use the command
git add README.md .gitignore git status On branch master No commits yet Changes to be committed: (use "git rm --cached <file>..." to unstage) new file: .gitignore new file: README.md Untracked files: (use "git add <file>..." to include in what will be committed) .vscode/ __pycache__/ square.py
The README.md file is used as the front page of the Github webpage, adding one to any folder will also make that a front page. It is written in a syntax called Markdown and in particular GitHub has it’s own version. Markdown can also be used in jupyter notebooks for text so it is well worth learning the basic syntax.
# This is a title Normal text is here ## Subtitle This is a subtitle, for the rest of the examples I will edit the file, as this webpage is written in markdown so embedding is harder :-)
This file is used by git to ignore certain types of file when adding, these are usually language / project specific, however we can add global setting as well.
For ease I usually copy a pre-prepared one from other projects. The contents of which is shown below. GitHub also offers pre-prepared templates here of which the one below is modified from.
# vs code project setup files .vscode # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class .vscode/ # stubgen files and MyPY out/ .mypy_cache/ # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST .pytest_cache/ docs/build # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ # Cython debug symbols cython_debug/
To take a snapshot of the code we use the git commit command. Typically we do this once a significant part of the work, or tests have been completed. There is no real set time to do this but it helps to do this frequently to be able to revert to older versions etc.
All commits usually have a commit message there is an art to writing good commit messages and some companies have style guides on how to do these. In general make them describe what has been done, what bugs are fixed etc.
git commit -am "added first programs to git" [master (root-commit) 17476a0] added first programs to git 4 files changed, 88 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 __init__.py create mode 100755 square.py
adding some code
We are now going to add some code to the square.py file.
def square(t, start_x,start_y,width=10): t.penup() t.goto(start_x,start_y) t.pendown() for i in range(4): t.forward(width) t.right(90)
git commit -am "update the square function to add position" [master 0ec50d6] update the square function to add position 1 file changed, 5 insertions(+), 2 deletions(-)
I’m now going to add some garbage to the file and save. We can now run
git diff diff --git a/square.py b/square.py index acfc6c2..48d2fc4 100755 --- a/square.py +++ b/square.py @@ -11,7 +11,7 @@ def square(t, start_x, start_y, width=10): t.forward(width) t.right(90) - +sdfkjadhsakjsdhsakjdh if __name__ == "__main__": my_turtle = turtle.Turtle() square(my_turtle, 200, 100, 100)
If we just want to get back the original file, we can use the git checkout function
git checkout square.py Updated 1 path from the index
reverting a commit
I’m going to add a new function to the file
def totally_broken_function() : print "this is broken"
I will then commit it
git commit -am "added a new function" [main 8baf6a0] added a new function 1 file changed, 3 insertions(+)
As this function is broken we may want to revert back to the previous commit
git log commit 8baf6a0f6c27adf82b602437d9d8e167c396f44f (HEAD -> main) Author: jmacey <email@example.com> Date: Wed Sep 14 14:14:31 2022 +0100 added a new function commit 2fd7e9c27c92e5de871deebf260cec6a257281aa Author: jmacey <firstname.lastname@example.org> Date: Wed Sep 14 14:06:31 2022 +0100 working demo framework commit d9d3d7389c7800d9f2c17e99d945eaf0311e689c Author: jmacey <email@example.com> Date: Wed Sep 14 12:42:04 2022 +0100 initial commit of README.md and .gitignore file
The log shows us the full history, you will see a hash we should be able to use this to go back to a previous snapshot.
git revert [hash]
Branching and merging
The typical workflow when using git is to create a new branch when adding / testing new stuff. We can create a new branch then check it out as follows
git branch filledsquare git checkout filledsquare Switched to branch 'filledsquare'
I’m now going to modify the code and add some new stuff.
def square(t, start_x, start_y, width=10,colour="red"): t.penup() t.color(colour,colour) t.goto(start_x, start_y) t.pendown() t.begin_fill() for i in range(4): t.forward(width) t.right(90) t.end_fill()
git commit -am "added some new functionality" [newfunctions 19ec737] added some new functionality 1 file changed, 5 insertions(+), 3 deletions(-) mode change 100644 => 100755 demo.py
Now we have done this we can re-checkout the main branch.
git checkout master Switched to branch 'master'
Typically if this worked we could now merge this into the main branch.
git merge filledsquare Updating 0ec50d6..6c567ff Fast-forward square.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
Typically we now commit this to the main branch.
At present all of this work is on the local machine. By using GitHub we can upload our work to a repository. The easiest way to do this is by creating a new repo in the web interface, then add this as the origin. The instructions are on the webpage and I will do this as a practical example in the lab.
The following examples will help us to understand more of the maths module and try to implement some simple algorithms. It is important to understand that most computers deal with cos,sin and tan functions in radians so we may need to convert from radians to degrees and back.
To do this we can use the following
To find the inverse of the cos and sin we can use the following.
Using the square.py example create a new file called ellipse.py and write a function called ellipse().
It’s signature will be as follows
def ellipse(t : Turtle, start_x : int, start_y : int, r1 : int r2 : int, step : int = 15) :
The algorithm to draw an ellipse can be implemented with the parametric function as follows.
- move to the start x,y (pen up)
- calculate the new x,y position for theta = 0 -> 360 in steps x
x=r1 * cos(theta) y=r2 * sin(theta)
- add step to theta
Using the previous example, create a new file called polygon.py
In this write the following functions and implement the algorithms.
- an polygon function, this will draw an n sided polygon of lenght l
def polygon(t : Turtle, start_x : int , start_y : int , sides : int =3, length: int=10):
- Using the SSS triangle formula discussed here write a triangle function that takes in three lengths a,b,c and draws a triangle.
def triangle(t : Turtle, start_x : int , start_y : int, a : int , b : int , c: int)
Try to add a rotation parameter to each of the drawing routines. This should apply a 2D rotation to the x,y co-ordinate pairs being drawn.
The equations to do this is
x= x * cos(theta) - y * sin(theta) y= x * sin(theta) + y * cos(theta)