if .. else
and for
list
and dict
Bootstrapping Python
turtle
library in Jupyter notebookPython offers a rich, vast collection of libraries to its users. Some of these libraries are built-in, while others require installation. To use a library, we first need to import it.
1 2 3 4 | from turtle import Turtle, setup, clearscreen
setup(width=500, height=500)
clearscreen()
tom = Turtle()
|
Sometimes you see people import this way:
1 2 3 4 | import turtle
turtle.setup(width=500, height=500)
turtle.clearscreen()
tom = turtle.Turtle()
|
A variable in Python is similar to one in algebra - it holds a value, and can hold any value.
1 | tom = Turtle()
|
tom
is a variable, =
the assignment operator, and Turtle()
the value assigned.Turtle
only because we imported Turtle
in the previous line.Turtle()
is an object of the Turtle
class. An object packs a lot of functionalities inside it.You make an object do things by calling its methods (or functions).
1 2 3 4 5 | tom = Turtle()
tom.forward(100)
tom.right(90)
tom.forward(100)
tom.right(90)
|
forward
and right
are methods (or functions) of the Turtle
class.forward
method's parameter specifies the distance to go. The right
method's parameter specifies the degrees of angle to turn.1 2 3 4 5 6 7 8 | tom = Turtle()
# Sets the speed (0-10) of the turtle
# 1: slow, 5: normal, 10: fast, 0: fastest
tom.speed(0)
tom.forward(100)
tom.right(90)
tom.forward(100)
tom.right(90)
|
Note: this is just one of the many possible answers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | tom = Turtle()
# Sets the speed (0-10) of the turtle
tom.speed(0)
# the bottom side
tom.forward(100)
tom.right(90)
# the right side
tom.forward(100)
tom.right(90)
# the top side
tom.forward(100)
tom.right(90)
# the left side
tom.forward(100)
tom.right(90)
|
for
loopWe can simplify our square drawing with for
loop.
1 2 3 | for i in (1, 2, 3, 4):
tom.forward(100)
tom.right(90)
|
i
is called the loop variable - its value is updated at the beginning of each iteration.Try adding the following line in our square-drawing loop
1 2 3 4 | for i in (1, 2, 3, 4):
tom.write(i) # <-- add this line
tom.forward(100)
tom.right(90)
|
Hint: use 80 as side length so Tom doesn't fall over the canvas
1 2 3 | for i in (1, 2, 3, 4, 5, 6, 7, 8):
tom.forward(80)
tom.right(360/8)
|
We can simplify the answer even further by using range()
1 2 3 | for i in range(1, 9):
tom.forward(80)
tom.right(360/8)
|
Hint: Can you draw a hexadecagon (16-gon) first? What does the result tell you?
1 2 3 | for i in range(1, 361):
tom.forward(1)
tom.right(1)
|
1 2 3 4 | tom.color('red')
for i in range(1, 361):
tom.forward(1)
tom.right(1)
|
.color()
method set the pen color.1 2 3 4 5 6 | tom.color('red', 'green')
tom.begin_fill()
for i in range(1, 361):
tom.forward(1)
tom.right(1)
tom.end_fill()
|
.color()
method can also set the fill color..begin_fill()
and .end_fill()
methods to indicate when filling should begin and end.1 2 3 | for i in range(1, 6):
tom.forward(200)
tom.right(180-180/5)
|
1 2 3 4 5 6 | tom.color('red', 'blue')
tom.begin_fill()
for i in range(1, 6):
tom.forward(200)
tom.right(180-180/5)
tom.end_fill()
|
Due to intersecting lines, Turtle is confused about where to shade
1 2 3 4 5 6 7 8 9 | tom.color('red', 'blue')
tom.begin_fill()
for i in range(1, 11):
tom.forward(80)
if i % 2 == 1:
tom.left(36*2)
else:
tom.right(180-36)
tom.end_fill()
|
if .. else
does one thing or the other, but never both!%
is the modulo operator - it gives the remainder of the left operand divided by the right operand.i
is a non-negative integer, how many possible values of i % 2
are there?1 2 3 4 5 6 7 8 9 10 11 12 | for i in range(1, 14):
tom.forward(20)
if i < 7:
if i % 2 == 1:
tom.left(90)
else:
tom.right(90)
else:
if i % 2 == 1:
tom.right(90)
else:
tom.left(90)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | for i in range(1, 14):
if i == 7:
tom.color('red')
else:
tom.color('black')
tom.forward(20)
if i < 7:
if i % 2 == 1:
tom.left(90)
else:
tom.right(90)
else:
if i % 2 == 1:
tom.right(90)
else:
tom.left(90)
|
1 2 3 4 5 6 7 8 9 10 11 | tom.fd(100)
tom.lt(120)
tom.fd(100)
tom.lt(120)
tom.fd(100)
tom.lt(30)
tom.fd(100)
tom.lt(90)
tom.fd(100)
tom.lt(90)
tom.fd(100)
|
fd
instead of forward
, lt
instead of left
, rt
instead of right
How to draw several house with minimum effort? The answer: functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def house():
tom.fd(100)
tom.lt(120)
tom.fd(100)
tom.lt(120)
tom.fd(100)
tom.lt(30)
tom.fd(100)
tom.lt(90)
tom.fd(100)
tom.lt(90)
tom.fd(100)
house() # <-- calling (or invoking) the function
|
def
ined a function called house
.house
function has no parameters, as indicated by the empty ()
.1 2 3 | house()
tom.rt(90)
house()
|
Wouldn't it be nice to draw houses of a different size, using the same function?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def house(size):
tom.fd(size)
tom.lt(120)
tom.fd(size)
tom.lt(120)
tom.fd(size)
tom.lt(30)
tom.fd(size)
tom.lt(90)
tom.fd(size)
tom.lt(90)
tom.fd(size)
house(50)
tom.rt(90)
house(50)
|
Hint: use tom.fillcolor()
to set only the fill color.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def house(size, color):
tom.fillcolor(color)
tom.begin_fill()
tom.fd(size)
tom.lt(120)
tom.fd(size)
tom.lt(120)
tom.fd(size)
tom.end_fill()
tom.lt(30)
tom.fd(size)
tom.lt(90)
tom.fd(size)
tom.lt(90)
tom.fd(size)
house(50, 'red') #<- specify parameter by position
tom.rt(90)
house(size=50, color='green') #<- specify parameter by name
tom.rt(90)
house(color='yellow', size=50) #<- specify parameter by name
|
1 2 3 4 5 6 7 | house(50, 'red') #<- specify parameter by position
house(size=50, color='green') #<- specify parameter by name
house(color='yellow', size=50) #<- specify parameter by name
house(50, color='red') #<- mix 'n match
|
Create a function neighborhood
, e.g. neighborhood(num_houses=8, house_size=50)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def house(size, color):
...
...
def neighborhood(house_size, num_houses):
for i in range(1, num_houses+1):
house(house_size, color='black')
tom.rt(90)
# The 3 lines below simply moves the turtle to the left
# so we have room to draw the houses
tom.pu() #<-- Pen Up, i.e. do not draw when turtle moves
tom.bk(200) #<-- Backward
tom.pd() #<-- Pen Down, i.e. draw when turtle moves
neighborhood(num_houses=8, house_size=50)
|
The mayor mandates the 8 houses of this neighborhood be colored with the following colors, in this order: brown, pink, green, orange, purple, red, blue, yellow.
Think for a bit - how do you represent this data of colors?
Python's list can help!
1 2 3 | empty_list = []
odd_numbers = [1, 3, 5, 7, 9]
colors = ['brown', 'pink', 'green', 'gray', 'cyan', 'red', 'blue', 'yellow']
|
List can be used in a for
loop:
1 2 3 | colors = ['brown', 'pink', 'green', 'gray', 'cyan', 'red', 'blue', 'yellow']
for color in colors:
print(color + ' is cool!')
|
indexable
List items can be accessed via its position index
. Indexing in python is 0-based.
1 2 3 | colors = ['brown', 'pink', 'green', 'gray', 'cyan', 'red', 'blue', 'yellow']
for i in range(0, len(colors)):
print('color at index #' + str(i) + ' is ' + colors[i])
|
[]
to index into a list. Function len()
gives the length of a list.Hint: Modify your neighborhood
function to accept a house_colors
parameter.
1 2 3 4 5 6 7 8 9 | def neighborhood(house_size, num_houses, house_colors):
for i in range(0, num_houses):
house(house_size, color=house_colors[i])
tom.rt(90)
tom.pu()
tom.bk(200)
tom.pd()
colors = ['brown', 'pink', 'green', 'gray', 'cyan', 'red', 'blue', 'yellow']
neighborhood(num_houses=8, house_size=50, house_colors=colors)
|
Can you remove the num_houses
parameter, and instead infer the number of houses from the number of colors provided?
Your neighborhood
function should now start like this:
1 2 3 | def neighborhood(house_size, house_colors):
...
...
|
1 2 3 4 5 6 7 8 9 | def neighborhood(house_size, house_colors):
for i in range(0, len(house_colors)):
house(house_size, color=house_colors[i])
tom.rt(90)
tom.pu()
tom.bk(200)
tom.pd()
colors = ['brown', 'pink', 'green', 'gray', 'cyan', 'red', 'blue', 'yellow']
neighborhood(house_size=50, house_colors=colors)
|
Families are moving into the newly constructed neighborhood. Each family picks a favorite color to move into.
Brown: The Abbotts. Pink: The Beavers
Green: The Craines. Gray: The Danubes
Cyan: The Edwards. Red: The Fishers
Blue: The Grammes. Yellow: The Higgins
The mayor wants their names displayed on the neighborhood map. The problem is - the mayor hasn't decided on which color gets painted on which house yet.
How do we link the colors to each family? We don't know which house is going to get which color yet, i.e the house_colors
parameter can change.
Python's dict can help you represent data that has key-value relationships.
1 2 3 4 5 6 | family_colors = {'brown': 'Abbotts', 'pink': 'Beavers', 'green': 'Craines',
'gray' : 'Danubes', 'cyan': 'Edwards', 'red': 'Fishers',
'blue' : 'Grammes', 'yellow':'Higgins'}
another_dict = {} # <-- this is an empty dict
another_dict['some_key'] = 'some_value' # <-- adding data into this dict
another_dict[100] = 200 # <-- key and value can be anything
|
For a dict D
, you can access the value associated with the key K
by doing D[K]
.
We can use a for
loop on a dict.
1 2 | for key in family_colors:
print('%s = %s' % ( str(key), str(family_colors[key] )))
|
Modify the house()
function to accept a new parameter name
. Print the name on the house.
Hint: use tom.write()
to print on the canvas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def house(size, color, name):
tom.fillcolor(color)
tom.begin_fill()
tom.fd(size)
tom.lt(120)
tom.fd(size)
tom.lt(120)
tom.fd(size)
tom.end_fill()
tom.lt(30)
tom.fd(size)
tom.lt(90)
tom.write(name)
tom.fd(size)
tom.lt(90)
tom.fd(size)
|
Make the neighborhood()
function take a new parameter families
, a dict of color and family names.
Then pass family names to the modified house()
function.
1 2 3 4 5 6 7 8 9 10 11 12 13 | def neighborhood(house_size, house_colors, families):
for i in range(0, len(house_colors)):
color = house_color[i]
house(color=color, size=house_size, name=families[color])
tom.rt(90)
tom.pu()
tom.bk(200)
tom.pd()
colors = ['brown', 'pink', 'green', 'gray', 'cyan', 'red', 'blue', 'yellow']
family_colors = {'brown': 'Abbotts', 'pink': 'Beavers', 'green': 'Craines',
'gray' : 'Danubes', 'cyan': 'Edwards', 'red': 'Fishers',
'blue' : 'Grammes', 'yellow':'Higgins'}
neighborhood(house_size=50, house_colors=colors, families=family_colors)
|
The mayor changes his mind - he wants to reverse the order of the house colors:
No big deal, our code still works, and families get to keep their colors.
1 2 | sroloc = ['yellow', 'blue', 'red', 'cyan', 'gray', 'green', 'pink', 'brown']
neighborhood(house_size=50, house_colors=sroloc, families=family_colors)
|
Table of contents | t |
---|---|
Exposé | ESC |
Autoscale | e |
Full screen slides | f |
Presenter view | p |
Source files | s |
Slide numbers | n |
Blank screen | b |
Notes | 2 |
Help | h |