BRIAN YU: We'll go ahead
and get started today.
Welcome back, everyone, to
the second day of CS50 Beyond.
So for just a recap for those of
you who weren't here yesterday,
or just as a refresher,
yesterday we introduced
the world of building web
pages, looking at HTML and CSS,
diving a little bit beyond
what CS50 originally taught,
looking at things like
mobile responsiveness, things
like media queries for determining how
styling should look on a big screen
or on a small screen,
looking at SaaS, a technology
we can use to generate CSS
code based on variables
and nesting and other different, more
advanced features that CSS by itself
doesn't offer.
We looked at different features
of HTML5, more advanced ideas
like the data list for
doing auto completion.
We looked at regular
expressions which are
ways of using a pattern to
define a set of strings that
are of accepted inputs, for instance,
that you can use in an input
field as a pattern when you're trying
to validate some sort of input,
for instance.
And then we also took a look at
Git, a version control software
that we can use to track changes to
code, keep track of modifications
so that we can go back, branch
off, and try different things.
So we covered a lot of topics yesterday.
Any high level questions
about yesterday's
topics before we dive into today?
If you're having specific
technical problems,
feel free to flag down a staff
member during project time.
We're definitely happy
to help you with that.
But questions about anything as you were
thinking about these ideas yesterday
that didn't come up?
OK, well, definitely feel free to
bring them up if they do come up.
Goal of today is going to be to
explore, moving forward, web application
design with Python and Flask.
So Python and Flask are both
ideas that we saw in CS50--
Python being the language that we
were using to build web applications--
but also, more generally, a very
useful practical programming language
that's quite popular nowadays.
And then Flask being
a framework in Python
that we can use in order to build
web applications easily, efficiently,
and effectively.
So we saw both of them in CS50.
We'll do a bit of review of them
in case you're a bit rusty on them
in order to be a bit of a refresher.
We'll also look at some more
advanced and more sophisticated
techniques and concepts.
Definitely feel free to stop
me at any point with questions.
And of course, if you go to the
course website as you did yesterday
and click on that feedback link, and
just have that open on your computer,
definitely feel free to use
that as a feedback mechanism
today as we go through today's topics.
If things are making sense,
press the green button.
If things aren't making
sense, press the red button.
That just lets me know--
perfect-- that you're following
along with things that I'm saying
or if you need some clarification,
need me to slow down,
or explain something in
a slightly different way.
Structure of the day
is going to be pretty
similar to what we did yesterday.
We'll have some morning time to talk
about Python and Flask web application
development.
I'll provide some examples.
Then we'll have a bit
of a morning project.
This one will be a little more
structured than yesterday,
where it was sort of free form and you
could choose whatever you wanted to.
We'll propose a project
for you to attempt to try.
We'll start it together so that you
can get a sense for the basic building
blocks, and then leave most of the
features up to you to implement.
And then later this afternoon we'll take
a look at some other Python features,
looking at the world of
object oriented programming,
another programming paradigm you might
be familiar with from other languages,
like Java for instance,
taking a look at how
you can use Python in object
oriented programming, which
is quite popular nowadays.
And we'll also take a look at
deploying websites to the internet.
And in particular, we'll look
at building web-based games
and look at the idea of
artificial intelligence,
something that might come up if you
took a class like CS182 here at Harvard,
which is the artificial intelligence
class, looking at ideas of,
OK, how do you program a
computer to behave intelligently.
And in particular, in the
context of a web-based game,
how can you program a
computerized AI to be
able to figure out the best move to
make in any particular situation.
So we'll take a look at that
later this afternoon as well.
But all of that awaits.
Questions about directions that
we're going today or anything
that I've covered so far?
All right, with that, we'll
go ahead and get started
by just taking a brief look at Python.
The beginning of this
is going to be largely
review for if you remember in
programming in Python from way
back in the days of CS50.
And so the most basic Python
application that we could write--
I'll create a new file,
we'll call it hello.py--
just looks something like this.
We would say "print"
and then "hello world."
Simple programming like this.
But in the context of this, there are
at least two important ideas here.
Print, in this case, is a function.
And functions you can just
think of as these abstractions,
these pieces of code that
perform some sort of behavior.
And I say abstraction in the
sense that I don't really
care how Python has actually
implemented the Print function.
I just need to know that the Print
function exists and I can provide it
a string, some sequence of
characters like "hello world,"
and when I run this program--
and I can run the program by running
a command just like Python, hello.py--
when I run that program, I
end up getting "hello world"
printed to the terminal screen.
So fairly straightforward.
And that was the most basic Python
application that we could write.
But, of course, like all
programming languages,
Python has many more
features than just that.
And one key idea that
we wanted to explore
was the idea of variables in Python.
And variables in Python, if you recall,
from CS50, differed from variables in C
in what way?
There are number of ways, but what's one
important way in which variables in C
and variables in Python were different?
Yeah.
AUDIENCE: [INAUDIBLE]
BRIAN YU: Yeah, you
don't need to declare
the types of variables in Python.
And Python has a number of
different variable types.
So I have integer types.
So I could say a equals
28, for instance.
There are floating point
types, just like C has.
I could say b equals 2.8, for instance.
And that's a floating point
number, a decimal number.
But it also has other types.
It has Boolean values,
like true and false.
So we would say c equals true.
Capital T for true, capital F for
false, is just the way that Python
happens to do Boolean variables.
It has a capital T for
true, capital F for false.
There are a number of
different types for variables.
Python even has what's called a
NoneType, which is a type used
to represent the absence of a value.
So I could set a variable equal to None.
And None just happens to be a value of
its own special type called NoneType,
that just represents the fact that
there is no value in a particular place.
So you might imagine situations,
which we'll in fact see later
on today, where it's valuable to have
some sort of representation of the idea
that you have no value stored
in a particular location.
Now, just because we
don't have to declare
the types of variables
in Python, doesn't
mean that those types don't exist.
And if you try and perform
operations on values
that the types don't line up
for, or don't quite match up for,
you could, in fact, run
into errors there as well.
So, brief recap of variables,
the idea variable types.
The next thing that we saw when we were
exploring Python in the case of CS50
was looking at conditions.
So we would see examples
like, all right,
if x is equal to a value like 10,
I could have conditions and say,
OK, if x is greater than 0,
then print x as positive.
Elif, was Python's shorthand for
else if, if x is less than 0, then
I'll print x is negative.
And then else, I'll print, OK, x is 0.
If it's not positive and
negative, it has to be zero.
So important things to
notice here are the keywords
if, elif, and else are the conditions
for if something is true, otherwise,
if something else is true,
else if something else is true.
And importantly all the code inside
of the if blocks are indented.
And this indentation
is important in Python.
Unlike other languages,
like [INAUDIBLE],,
where indentation and white space is
optional, in Python it is mandatory.
The indentation is how you tell
the program which pieces of code
are located inside of which blocks.
And so I could run this program, run
Python, hello dot pi, in this case.
And it says x is positive, which it is.
And now of course, this program is
going to behave exactly the same way
no matter what it is that I
have typed into the program--
because I'm not prompted to
type anything into the program.
So I could make this
program responsive to input.
I might want to prompt the user
for OK, please type in a number.
And when you type in
a number, then we're
going to tell you if it's
positive, negative, or zero.
And in CS50 how did we do this?
What was the function we used in order
to get input in integer from the user?
Anyone remember name of that function?
Name of the function
that gets an integer?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Someone said it.
Get int?
OK, I think I heard someone say it.
Get int was the function that we
used in the CS50 library in Python
in order to get an
integer from the user.
Of course, that function is
only in the CS50 library.
You can install the CS50 library.
It's pre-installed in CS50 IDE, but
you can install it elsewhere in order
to get access to that get in function.
But in practice, most
computer installations
don't have the CS50 library
installed by default.
And so how would you get input
from the user if you wanted to?
Well, Python 3, the latest
version of Python at least,
comes with a function called input.
And the input function
basically just gets input
from the user, not something
we really explored in CS50,
but something you can do.
So I could say something
like, OK, type a number.
I'm asking the user to type
in a number, prompting them
for some sort of input
setting, that value equal to x.
And then checking to see if x
is positive, negative, or zero.
And so I could run this,
run Python, hello dot pi.
Type in a number.
I'll type in 0 for example.
And OK, I get an error.
This is the first example of a
Python exception that we've seen.
And the type of this error, what
kind of exception has been raised--
is the terminology we'll use--
is a type error.
And here's the type error, greater than
symbol not supported between instances
of str, short for string, and it.
And so this is an
exception that highlights
what I was mentioning just a moment ago,
that even though we didn't give types
to variables explicitly, those
variables still have types.
Namely, what I'm comparing x
greater than 0, 0 is an int.
And x, it claims--
Python's interpreter is
saying-- is a str, a string.
And that's ultimately because this input
function asking me to type in something
at the command prompt is asking
for just any input, which
could have been a number, but it
also could have been some text.
I could have typed hello or
hi at the command prompt.
And it would've been valid input.
And so what I need to do in order
to make this work is something
called typecasting, the idea of
converting something from one type
into another type in order to make
it work. x right now is a string.
And I want to convert it
into an integer in order
to allow myself to do these comparisons.
Is x greater than zero or
less than zero or so forth?
And the easiest way
to do that is just use
a function called int in Python
that take something and convert it
into an integer.
So I'll wrap this input function
inside of an int function.
And so here is the idea that I'm
taking the output of the input function
and passing it as input
to the int function.
And so it's often useful to think
about functions in the way of what
are the inputs to this function?
What is the output of the function?
And thinking about trying to
relate those things together.
As you start to build more complex,
more sophisticated programs,
you'll begin to use
more and more functions.
And being able to reason about OK,
what is the input to the function?
What is the output to the function?
Is ultimately going to be quite helpful.
So now I type Python hello dot pi.
I type a number like zero.
x is zero.
It was able to convert
my input into an integer,
and then run some comparisons, as
a greater than or less than zero,
for instance.
If I type in a positive
number, 28, x is positive.
OK, great.
I type in a negative
number, a negative 50.
OK, x is negative.
It will be able to perform
these comparisons on the fly.
Now, of course, if I
type in something else--
I type in beyond, just some
string for instance-- now
I'm getting a different
exception that's getting thrown.
I'm getting a value error.
Invalid literal for the int
function with base 10 beyond.
In other words, it can't take the word
beyond, the string that I inputted,
and convert it into an integer.
And so what's Python
going to do about it?
It doesn't really know what to do.
So it raises an exception.
It causes an error.
And that error is a value error.
So we've seen a lot here.
We've seen getting input.
We've seen functions input and output.
We've seen Python exceptions
and how to handle those.
Questions about anything so far?
How do we feel about it?
If you could just use
the feedback for, let
me know are things
making sense generally?
Is this familiar?
All right.
Great.
Seems like most of this is
review, most of this familiar.
And we'll move on to
other data types as well.
Important in Python are
the idea of sequence data
types, the idea that data type don't
need to just contain one value.
They can contain sequences of values.
So I could have a name for instance.
I could say OK, name is going
to be Josh, for instance.
And Josh is us type str,
a string, but it's also
a sequence of characters,
whereby if I wanted to print out
the first character in
Josh's name, I could say
OK, print out name Square Bracket zero.
And if I were to run this program,
it would print out just the letter J,
printing out the first
item in this sequence.
And a string is the simplest example
of a sequence that we've seen.
It's just a sequence of one character
after another after another.
In addition to sequences
being strings of text,
we also have lists are also
examples of sequences in Python.
So I might say OK, we
have multiple names.
And so we have Krishna
and we have Athena
and we have Julia as examples
of a list of possible values.
And if I wanted to get the first
item in this list for instance,
I could say OK, print
names Square Bracket zero.
Remember that list as with
arrays and C are zero index.
The first element is item number zero.
And if I print that out, it prints out
Krishna, the first name on the list.
And I can say print names one to
get the second item in the list.
It gets Athena.
I print names two to get
the third item in the list.
That's Julia.
There are other tricks that
you can do here as well.
If you want to go
backwards for instance--
I don't want the first item in the list.
I want the last item in the list--
you can use negative numbers.
So if I say print names negative one,
for instance, that's going to say,
OK, let's go backwards and get
me the last item in the list.
So if I print out names
Square Bracket negative 1,
that's going to print out which name?
Julia.
Great.
So that's the last item in the list.
Prints that out because that's
Square Bracket negative 1.
The other important sequence
data type that will often see
is called a tuple or a tuple.
You'll hear it pronounced both ways.
And that's useful for
immutable sequences,
sequences that cannot change.
And so you might imagine for if
you have a fixed number of things
like a coordinate pair, like an
x-coordinate and a y-coordinate,
a tuple or a tuple might
make sense for here.
I say coordinates equals like,
2, 8 x-coordinate y-coordinate.
And now I have a first
item and a second item.
I can index into them
with square bracket
0 square bracket 1 to get it the
first and second items respectively.
And I can use that syntax as well.
This is also particularly
useful if you ever
need a function to return two values.
Normally, a function can
only return one thing.
But if that one thing is a tuple, you
can take that topple and split it up
into all its component parts.
So you have the ability to
effectively have multiple things
that you're returning from a function by
taking advantage of this functionality.
Questions about anything so far?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
So the question is why would you use
a tuple instead of a list of size two?
So first of all, tuples don't
necessarily need to be just two items.
They could potentially
be more items than that.
And so you could extend--
if I wanted a three
dimensional coordinate plane,
this could be two, eight,
five, for instance.
And you could have multiple
things in sequence.
It doesn't need to be just two.
It could be any fixed number of them.
But there are certain advantages, in
particular, the tuple is immutable,
meaning it cannot change.
And so that can help to defend
against certain kinds of errors
if you don't want to allow a data
structure to be able to change.
And there are certain
operations you can perform
on them that are more efficient.
So a lot of things are
just trying to figure out
which is the right choice of data
structure to fit the type of thing
that you're trying to do.
And we'll take a look at other data
structures in just a moment as well
and compare their advantages
and disadvantages.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
Question is do all the elements in
a tuple need to be of the same type?
No, they do not.
I could have two, eight,
and hello, and that would
be a totally valid tuple in Python.
They could be of any type.
Same is true of lists.
You can have them be of any type.
They don't necessarily
need to be the same.
However, oftentimes, in the
case of lists for consistency,
you will want them to
be of the same type,
because the general idea
of the semantics of a list
is you should hopefully
be able to treat all
the elements in the list the same way.
Usually, it's a list of things
that the same type of thing,
whereby you'll have a list of
names or a list of student objects.
And we'll look at object
oriented programming later.
And that's actually
another case where it
might be useful to differentiate
between a list and a tuple,
that frequently you
use a list when you've
got a whole bunch of things that
are of the same type of thing
and you want to treat them the same way.
And a tuple is often
more useful if you have
a bunch of different types
of things, but you still
want to encapsulate together
in a single representation.
Good questions.
Other things?
We'll talk about loops and
functions, and then we'll
dive into a couple
more advanced features.
So in order to loop over
a sequence of things--
so I have a list of names.
I have Krishna Athena, Julia,
and I'll add Josh there too.
If I want to loop over all of them,
the easiest way to do that in Python
is to say for name in names.
What I'm doing here is I'm saying I
want to loop over this array of names
and give each one the name name.
And I'll just print out that name.
I'm looping over my list of names,
calling each item in the list name
and printing that out.
So if I run this program, I get all the
names printed out one line at a time.
If I wanted to print something
out a little fancier,
I could say like, hello--
if I want to say hello
to the person's name,
I can use what's called a Python
format string, which you probably
remember from CS50 whereby I and
use these curly braces to say hello.
And then in Curly Braces name, I have
to put an F in front of the string
to say this is a formatted string.
But the curly braces
are basically signifying
I would like to plug in some
value here, in particular, I
want to plug in whatever the name is,
whatever the value of the variable name
happens to be.
And I'm printing that out.
So printing hello common name.
I print that out.
We get hello, comma, and then each
of the names, a formatted string.
And certainly, we'll see more
formatted strings later on too.
So you can use for name in names
to iterate this sort of idea
to iterate over any kind of sequence.
If instead of name, we
just had a single name.
And OK, a name equals Julia.
I could say for character
in name, let me go ahead
and print out the character.
Name as a sequence, it's
a sequence of characters.
And if I print out each character
one at a time, what I'm going to get
is just one character, one
on each line, one at a time.
So that works for those
types of sequences.
It'll work for tuples as well.
You can do this kind of iteration
over any type of sequence.
You can also iterate over
just a range of numbers.
A range is a particular
type of sequence.
So I could say for I in range 10,
print I. Range 10 is just a sequence.
It's going to generate numbers between
0 up through, but not including 10.
So 0 up through 9, for instance.
So if I say for I in range 10, print
I. That'll give me 0 all the way
through 9.
I can also change the starting
point of a range if need be.
So if I say for I in range 5,
10, that's going to start at 5.
Go all the way up through
10, but not including 10.
So I do that.
And actually, I'll just put both side
by side so you can see them both.
I get 5, 6, 7, 8, 9.
So it goes from 5 up through,
but not including 10.
And range can even take a
third argument if I want to,
which is the step amount,
in other words, how
much to increment by after each step.
So I could say I in range 0
through 100 stepping by fives.
So that's going to take me from 0 all
the way up to, but not including 100,
going five at a time.
And if I run that, I get 0, 5, 10,
15, 20, all the way up through 95,
because after the next
five it would hit 100.
So a bunch of different
ways to iterate over
sequences of values, whether
they're ranges or strings or lists,
or other types of sequences.
Questions about any of that so far?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Oh, good question.
So the question is what if
I wondered all the numbers
to show up on one line instead
of each one on a separate line?
So Python's print function by
default will always add a new line
after the end of the statement.
But you can change that.
And so we'll talk about
functions in just a moment.
But Python allows for functions to
have sort of optional arguments.
And in this case, print
has an option argument
called end, which is what should
be at the end of the line?
And the end value by default is the
new line character, backslash n,
which you might remember from CS50.
But I could change that.
I could make end be instead just the
space character, just one new space.
And I'll go from up to 30 maybe, so
as not to take up too much space.
But for if I run this program now,
now I get 0, 5, 10, 15, 20, 25, all
in the same line separated by spaces.
And you'll notice in my terminal
prompt is even on the same line,
because nowhere in my program did
I say OK, go on to a new line now.
It's always printing out a
space at the end of each one.
Good question.
Other things?
How about I take any like,
how do I do this in Python
if there are things that
you're wondering about?
All right.
We'll take a look at functions
and some data structures as well.
Actually, I'll do data structures first.
So oftentimes in Python you're going
to want to store data in some way.
And there are a number of
different data structures.
And it's important to have a grasp of
what the different data structures are
and what makes them different.
And so one of the simplest
data structures is a set.
So we have a list, which is an
ordered sequence of elements.
A set is an unordered
collection of elements.
And that collection
doesn't have any order,
and also all of the elements
of the set are unique,
much like a set in mathematics
if you're familiar with it,
a bunch of unique values that
are not in any particular order.
I can have a set of things.
S is a set.
And I can say something like, OK,
let's add the number one to the set.
And let's add the number 28 to the set.
And let's add the number 50 to the set.
And now let me print out
the contents of this set.
So I'll run this program.
And it's giving me this set
of things 1, 50, and 28.
Notice this is not the same order as
the order that I put these values in.
The set doesn't maintain
any sense of order.
It just knows OK, it's got the number
1 and 50 and 28 in it somewhere.
And that's useful to me if I
want that collection of things
to be inside the set.
And so now I can ask questions
like, print out if 28 is in the set.
Is the number 28 in the set?
Or not is 28 in the set.
And I'll try that.
OK, true.
Yes, 28 is in the set.
Is the number 29 in the set?
Try that.
Run that program.
False.
29 is not in the set.
And sets are useful, because looking
up whether something is in a set
is more efficient than looking up
whether something is in a list.
Looking up whether
something is in a list
happens in what we used
to call linear time.
Basically, if you want to
check if a value within a list,
you're going to have to look
through that entire list from start
to potentially to the end, looking
at one element at a time, checking.
Is this element in the list?
Is it in the list going all
the way from start to finish?
In the case of a set, the values
are stored slightly differently.
They're stored in something
akin to what a hash table is.
If you remember hash tables from CS50.
So it's much more efficient to look
up is this number inside of the set?
Much in the same way that
in CS50 speller problem
set you were able to use a
hash table potentially, to say
is this word in the dictionary.
And you were able to do that
efficiently by the use of a hash table.
So looking up whether
something is in a set,
ultimately more efficient than
doing that same look up in a list,
for example.
So that can be cases where a
set is oftentimes more helpful.
Now a set just stores a
whole bunch of values.
Oftentimes, in Python or in
JavaScript or in other languages
you'll use you'll want
to store not just values,
but you want associate
keys and values together.
In other words, you want
to store some sort of label
or some sort of identifier,
and some sort of value
for each one of those
identifiers, for instance.
And so this is the idea
of a Python dictionary,
or what you might
equivalently call a JavaScript
object, which we'll look at tomorrow.
But a Python dictionary is a
very important data structure
that will come up very, very frequently
that associates keys and values
that associate some sort of
name with some sort of value
associated with that name.
And so let's say I wanted to store
the ages of different people.
And so I might say ages is
going to be a Python dictionary.
And we have a name Alice.
And Alice is 22.
And we have a name Bob.
And Bob is 27.
And so here we've created a
Python dictionary called ages.
And in this dictionary the
keys are Alice and Bob.
They're the things that we're
going to use to look something up
in this dictionary.
And the value is what we're going
to get when we do that look up.
Like, how old is Alice?
If I wanted to ask that
question, I would print out
OK, let's take the ages
dictionary, and let me look up
Alice inside of that dictionary.
I would like to see how
old Alice is by looking up
what value is associated with the
Alice key inside of this dictionary.
So I run the program.
And OK, I get print out 22.
That is the value associated
with the key Alice.
And if I were to look up
Bob, run the program again.
OK, 27.
That's the value associated
with the Bob key.
If I were to look up Charlie,
at another name I get an error.
In this case, I got a
different type of error
that we haven't seen before, a
key error, meaning the key Charlie
isn't present in the dictionary.
I tried to look up a
key that did not exist.
And so I ran into an error there.
I could add Charlie to the
dictionary either by updating it here
or I can also update a dictionary
after I've already created.
I could say OK, age is Charlie.
Charlie is going to be 28, for instance.
And so I print age is Charlie.
And now OK, no more key error.
Charlie is in the dictionary.
And Charlie is 28 years old.
So Python dictionaries can
be used for that as well.
Questions about sets, dictionaries,
data structures, more generally?
Yeah?
AUDIENCE: Would you remove or edit
existing keys in the dictionary?
BRIAN YU: Great question.
Can you remove or edit existing
keys in the dictionary?
Absolutely.
So to edit a key for
instance, if I want to say
it's Alice's birthday, so
Alice's birthday, the hash tag
just get a comment.
We're going to the age
is Alice plus equals
one, meaning take whatever
Alice's age is, add one to it.
You could also just set it equal
to some other value entirely.
But now if I were to print out Alice's
age, we get 23, as you might expect.
I was able to update the key.
And you can also delete
something from a dictionary
by saying dell is the keyword here.
So for delete-- so if
I delete age is Bob,
and now let me just print out the ages.
What I get is Alice is 23 because
she had a birthday, 22 to 23.
Charlie is 28, as I said on line three.
But Bob is no longer
inside of this dictionary.
Remove that from the dictionary.
Good question.
Other things?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
So the question is can you
only go keys to values,
or can it go the other way around?
Long story, short, the dictionary is
only designed to go from key to value.
You look up the key
and you get the value.
But certainly, you can
write algorithms that
would give you the answer to
the question that you're asking.
So it is important to think about
which direction you want it to go.
Usually you want what you're
looking up to be a key that you're
going to know for sure,
and the value is the value
that you care about identifying.
Other things?
All right.
We'll look at a couple
more things before we turn
to actually building web applications.
Just want to re-familiarize
everyone with Python first.
We'll talk about functions.
And so functions-- I
might create a function--
let me create a new file
called functions dot pi.
And let's create a
function called square
that just takes a number and squares
it, returns the value of x squared.
And that function is going
to look something like this.
We define a function called square.
In parentheses is the input that
it takes, a variable called x.
And what we return from the
function is just a value x times x.
We multiply x by itself.
And now what I'd like to do is print
out the square of 10, for instance.
So I'm passing the number 10 as
input to the square function,
running that function.
Whoops, wrong file.
Functions dot pi, and I, in
fact, get the number 100.
Oftentimes, a common
paradigm that you'll see
is whatever the core code
of your Python script
does goes inside of a main function.
And you'll often see the main function
at the top of the file, actually.
And then at the bottom, you'll see
some lines that look like this.
If name equals main, then
run the main function.
And what line seven and
eight are basically saying
is if name equals main
is asking am I running
this file is the script that I'm
running, as opposed to the script
that I'm importing elsewhere?
And if so, then go ahead
and run the main function.
So if I run this function it's
going to produce the same output,
just produce the number 100.
But the reason why I might
put things in a main function
separately is because
in a separate file now
I could say something like,
all right, from my function's
module, this other file located here,
let me import the square function.
And then I can do things with it.
Let me for I in range 10, I'll
print out I squared is square I.
Basically using a formatted string,
taking whatever the value of I is
and saying I that value squared
is, and then in curly braces
plugging in a new value,
plugging in the value
of applying the square
function to the value I
to get whatever the output of that is.
And if I run Python hello dot pi, I
end up getting 0 square to 0, 1 squared
is 1, so on and so forth, because I'm
able to use the square function defined
elsewhere.
But if in functions dot pi, I
hadn't done this main function thing
and I just said OK, let me print out
square of 10 at the bottom of it,
and then when I run
Python hello dot pi, I'm
going to get this extra 100
printed before any of the stuff
I actually care about in hello dot pi.
Because this 100 happened when
I imported functions dot pi,
and it was running the
code from top to bottom.
It ended up running this print
square of 10 line as well.
So the reason why we put
things in main functions
is so that we can import that
module into other modules
and still be able to
use all those functions.
So if you're wondering why we saw that
in CS50, that would be part of why.
Questions about functions?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: The F is for formatting.
So the F in print stands
for formatted string.
And I use that because I want to be
able to use these curly braces to say
plug in some value here.
I want to plug in I and I want to
plug in the value of the square of I.
And those are Python values that
I want to insert into the string.
If I didn't have the F there and I
tried to run this, what I would get
is literally printouts of Curly Brace
I squared is Curly Brace square I.
And that's of course, not what I want.
Good question.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: So the question is what is
exactly if name equals main doing?
What it's doing is it's not going
to run the main function unless this
is the script that I am running.
So any code inside the main
function, whether they're
print statements or calls to
other functions or whatnot,
aren't going to run unless I'm
actually running functions dot pi.
And so if I'm importing it, then
that code just never is run at all.
AUDIENCE: [INAUDIBLE]
BRIAN YU: If any variables that
are set inside the main function,
those are not going to get
carried over in the import.
AUDIENCE: [INAUDIBLE]
BRIAN YU: Right, right.
I think there's a question
over here somewhere?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
So the question's about importing
things from different locations.
That gets a little bit trickier.
It has to do with sort of where
Python is looking for files.
There are ways of getting around it.
You can import from sub-directories or
you could sort of go backwards in order
to get something from
a parent directory,
and then go into sub-directories.
But that tends to be a little
less common, but possible.
So one last thing I'll touch
on that we didn't really
get a chance to talk about in
CS50, but that's good to know,
is this idea of how to
handle these exceptions.
We've seen a lot of exceptions
or errors in Python.
We saw the key error when I tried to
look up Charlie in the dictionary,
but Charlie didn't exist.
I saw the error that I got when
I was trying to compare a string
and an int to see one was
greater than the other.
And we dealt with all these things.
There are all sorts of different errors.
A really easy error to create
is x equals 5 divided by 0.
Division by 0 is not allowed.
So what's going to happen
if I try to divide by 0?
If I try to run Python hello dot
pi, I get a special exception that's
just called a zero division error.
It's like, OK, you can't divide by zero.
Sorry about that.
And so what could you do to try and
deal with these potential exceptions?
Well, one way you could try
and deal with this problem
is to add all these checks.
So if I said OK, x equals 5, y
equals 0, z equals x divided by y,
I could write a check to
say OK, if y is equal to 0,
then print like, sorry else, then
do z is equal to x divided by y.
And we could check for the
errors before they happen.
But a common Python paradigm is just
to let the exceptions happen and deal
with the exceptions when they come.
And so the syntax for that's
going to look something like this.
I'm going to say something like,
try z equals x divided by y.
And when I say put everything inside
of a try block, what I'm saying
is run this code.
But I'm going to try to run this code.
Like, there might be some
sort of exception or error
that comes about when
I'm running this code.
So let's just try it
and see what happens.
And run it normally.
Except if we get this particular
exception, the zero division error,
then go ahead and print out
sorry, you can't divide by zero.
So we're saying inside
of this except block,
if a zero division error happens and
the exception takes place, what is it
that I want to do about that situation?
Well, in this case I just
want to print something out.
And so I go Python run this program,
and immediately I get sorry,
you can't divide by zero.
Instead of getting some error,
like the zero division error,
it just said OK, sorry.
You got a zero division error.
It went down to the except block
and then it printed this out.
And so you can imagine that this
could be potentially useful.
If we go back to our example
from earlier whereby I had ages
and I had Alice was 22 and I had
Bob was 27, and I say OK, name.
Let's get some input.
Who do you want to look up?
And I'm going to print out a string.
I'm going to print out name
is ages name years old.
I'm printing out someone's
name is, and then
looking them up in the age's
dictionary with the name
is the key, getting the
value, which is the age,
name is this number of years old.
And let's print that out.
And so I try that.
Who do you want to look up?
I want to look up Alice.
Alice is 22 years old.
Great.
I want to look up Bob.
Bob is 27 years old.
All right, I want to look up Charlie.
Key error, Charlie.
This is the key error we saw before.
Charlie is not in the dictionary.
So when I try to look up
Charlie, it doesn't quite work.
So how can I deal with the situation?
Well, one way I could
deal with it is OK,
if the name is in the age's dictionary,
then print it out, else, print sorry,
name is not in the
dictionary or in the--
yeah.
It's not in the dictionary and
make that a formatted string.
So I could do that.
If name and ages is asking
is this person's name
inside of this dictionary of ages,
and if I run this and type in Charlie,
I get OK, sorry, Charlie
is not in the dictionary.
But a more efficient
and what I might call
a more Pythonic way of writing the
same code is just to say you know what?
Try to do this.
Try to print out this name
is this number of years old.
Except if we get a key
error, then that must
mean that we couldn't find the person's
name in this dictionary of ages
that we were looking up.
And we can say OK, sorry, this
person's name is not in the dictionary.
Sit around Python hello dot pi.
Who do you want to look up?
We want to look up Charlie.
OK, sorry, Charlie is
not the dictionary.
And so this exception handling
mechanism is something
you'll see quite frequently in Python.
Python is designed to let you
take advantage of this feature
very readily and very easily.
And so you'll often see people
try and deal with errors
in this particular way of
just trying code and then
catching that error if need be.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
The question is, do you need to define
what type of error you're looking for?
You can be less specific if you want.
I'm being very specific in saying
when you get a key error exception,
you should deal with it in this way.
But you could generalize it to say
except any sort of general exception
in order to catch errors more broadly.
But then you need to
figure out a way of dealing
with many numbers of
different kinds of errors.
So generally, if you
can be specific, it's
a good idea to be specific about
what it is that went wrong.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Yeah.
So we could do--
so the question is what if
I wanted to differentiate
based on two different types of errors?
So let's do a key error.
So name is this number of years old.
And let's say for whatever reason I
wanted to set x to 10 divided by age
is name.
I don't know why you
would want to do this.
I'm just sort of creating
a contrived example where
we might get two types of errors.
But the type of error we
might reasonably get here
is a zero division error.
If for instance Charlie is 0 years
old, and I said OK, run Python.
Who do I want to look up?
Alice.
Alice is 22.
If I look up someone who's not in the
dictionary like David, I get sorry,
David it's not the dictionary.
But if I look up Charlie, I
get Charlie is 0 years old,
but then I get to zero division
error, a different type of exception.
And I can handle that separately.
I can say except zero division
error and print sorry,
the age is 0 or something like that,
handle both of those exceptions.
So now if I type in David, I get
sorry, David is not the dictionary.
But I type in Charlie, and I
get Charlie is 0 years old.
And then instead of getting a
zero division error, I get sorry,
the age is 0.
So you can chain these
except blocks together.
It's saying if I get this
type of error, do this.
If I get that type of
error, do something else.
And so you can exception
handle in that way as well.
All right.
Covered a lot of topics just now.
How do we feel about that generally?
All right, mostly feeling good.
Fantastic.
In that case, let's go
ahead and turn to Flask.
A question first before
we turn to Flask.
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
So the question is what if I want to
do more code after all this exception
handling?
You can just add it after that tries
and excepts are done and just don't
indent them.
I can say this is the
end of the program.
And then no matter what
happens, if it works, I type,
what do you want to look up?
Alice.
Alice is 22.
This is the end of the program.
I look up David.
David's not the dictionary.
Still get to the end of the program.
So any code after that
try except will still run.
Other things?
All right, so let's go ahead
and create a Flask application.
And so recall that Flask
was a Python framework
that we can use to very easily and
very quickly build web applications
and begin to scale down.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Yeah sure.
Hello dot pi.
And if you want to see
this as well, I haven't yet
posted the source code
examples for this morning,
but I will soon as soon as we're
done with this morning session.
All right.
So Flask is what we're going to use
in order to build web applications.
We saw a little bit of it in CS50.
But we're really going to dive more into
it today and tomorrow, in particular,
and the day after as well.
And so in order to create
a Flask application,
we're going to store code in a
file called application dot pi.
And the simplest of Flask
applications, as you might recall,
looks something like this,
from Flask import Flask--
so you'll need to install
Flask on your computer.
If you have the Python
Package Manager, you
can just write PIP or PIP3 install Flask
to install flask under your system.
And I'm going to say
app equals Flask name.
In other words, create a
Flask app from this file.
I want this file to be the
host for a Flask application.
And the entire internet is based
on this idea of routing, the idea
that if I go to some website
slash a particular route, that
takes me to some different
logic on that website.
If you go to CS50 dot
Harvard dot edu you
get the normal CS50 college web page.
But if you go to CS50 dot Harvard
dot edu slash beyond, go to the slash
beyond route, then you're taken
to this course's web page.
And so depending on your
route, different logic happens,
the user sees different content.
And so the entirety of
Flask is based on this idea
that if different routes
happen, we should be running
different code or different functions.
And so I'm going to say
at app dot route slash.
In other words, if I just
go to the slash route,
what is the code that should run here?
And I'll say OK, we'll run a function
that I'm just going to call index.
Could be called anything.
The function name is sort
of irrelevant, that you
can reference things by it, which
we'll maybe see a little bit later.
And here I can say OK,
return hello world.
Very simple, very basic
web application that's
basically saying if someone goes to
the slash route of my web application,
I want to say hello world to them.
I can run this application
by running Flask run.
And so I run Flask run.
I end up getting this URL.
I'll go out and copy this URL,
paste it into a web browser.
And what I see is hello world.
I didn't go to any particular route.
I just went to the default route,
and I get hello world as a result.
Now of course, I could add
other routes to this as well.
If I wanted to, for instance, add a
route that didn't just say hello world,
but said hello to someone in
particular like Julia, for example--
Julia is sitting right in front of me.
So she's in all my examples today--
app dot route slash Julia for instance,
I can run a special function
which is called Julia
that's going to return hello Julia.
And so if I run this
web application now--
I'll go ahead and restart it--
or it probably restarts automatically--
but I refresh the page and
it still says hello world.
But if I change the URL, instead
of just going to the default route,
I go to the slash Julia route and press
Return, then I get a different page.
This page says hello Julia instead.
So two different routes, each of which
is connected to a different function.
And each function return
some different information
that gets sent back to the
user who's viewing the page.
Questions about that so far?
That much at least should
be review from CS50.
Now of course, there's a
little bit of a problem here
in that this is not a
particularly scalable solution.
So like, Athena just walked
in the door, for instance.
And if I wanted to say
hi to Athena, I might
want to go to this web application
and go to web address slash Athena.
But when I do so, I get
a 404 error, not found.
And I got this error because slash
Athena is not a route on my website.
I can't go to slash
Athena because I haven't
written a rule that says when I go to
slash Athena, what code should I run?
What should I actually do?
So we can generalize
this idea of instead
of just having a slash Julia route
the just says hello to Julia,
maybe I want to be able to handle not
just fixed routes, but variable routes,
routes that take in some
variable as their argument,
and then produce some
sort of different output.
And so rather than just saying this
is the route that is slash Julia,
I'll say this is a route that's
going to take a string value,
and I'm just going to call it name.
This function is going to be
called hello instead of just Julia.
And hello is going to take an argument.
It's going to take the
argument called name,
where this name matches up with the
variable that's inside of the route.
And so what I'm doing here
is creating a variable route.
This isn't just going
to accept slash Julia,
but it's going to
accept slash any string.
That string is going to be called name.
And that value name is going to be
passed in to the hello function.
And so now, rather than returning
hello Julia, I can return hello
and then a formatted string plugging in
that name into that spot in the string.
And so OK, what does that going to do?
Let's go ahead and run
the web application.
I go to slash Athena.
And it says hello Athena.
It's taking the route that
I plugged into this URL,
and it's just placing it into
the formatted string that
gets shown to the user.
I type in slash Josh instead,
and it says hello Josh.
I type in slash Krishna,
it says hello Krishna.
And so I can do this with any string.
And it's just going to give me
the result here on that page.
Now of course, this isn't quite
ideal because I would probably
like for the names to be capitalized.
It would look a little nicer
for the user interface.
But that's fine.
I can use any Python
logic I want in order
to be able to generate
the page that I want.
So I could do something like
name equals name dot capitalize.
And I happen to know the
Python has a built in function
called capitalize that takes
a string and capitalizes it.
So I do that.
And when I go to slash
Krishna now, now I
get OK, capital K. I go to
slash Athena, capital A, Athena.
And so here here's the web
application that's doing that.
I have a variable root that accepts
some arbitrary string that's its input.
And I can go to that arbitrary string.
And when I do so, we're
able to take some variable,
make some modifications to it using
any arbitrary Python logic we want,
capitalizing some name, for example,
and then returning that result.
Questions about that?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Great question!
Question is if I still
had a slash Athena route,
which one is it going to do?
So let's say I have an app dot
route slash Athena that actually
is an Athena route, definitely Athena.
And this one returns some other content.
I don't know what yet.
But at the end, it gets
some other content.
She likes otters.
Athena gets otters.
This is the other one.
So now what's going to happen
if I go to slash Athena?
Am I going to get hello comma name?
Or am I going to get some other
content Athena likes otters?
Well, Flask is going to try
to be intelligent about it.
Flask is going to go to the
more specific route if it can.
And so therefore, if I go to Athena,
what I get is some other content
Athena likes otters.
That is the more specific route,
as opposed to the more general one,
if we just went to the slash name one.
So if I go to slash Julia,
Julia does not like otters.
It just says hello Julia.
I don't know if Julia likes otters
or not, but it's what we get.
Good question though.
Other things?
Yeah?
AUDIENCE: So when you find
a variable [INAUDIBLE]??
BRIAN YU: Good question.
So the question is like, what
is this string doing here?
Does it need to be there?
It does need to be there.
So we do need to define what
the type of the argument is.
And the idea here is that
that's going to allow
us to be able to be more
specific about our routes,
that sometimes we might want
for example, an int to be there,
where you imagine a newspaper
website for example,
and you want to go to yesterday's
paper, you might go to like,
name of the newspaper.com slash 2019
slash one slash whatever the date is.
You have some custom
route that does that.
And you can imagine that this
variable routing gives you
a lot of expressive
capability that we couldn't
have with just fixed
routes where you imagine
that sites like Twitter or Facebook, for
instance, where you go to Twitter.com
slash the person's Twitter handle.
Twitter doesn't have
some engineer that's
writing out routes for every
single possible user on Twitter.
They just have some variable
route that accepts any user name.
And when any user name is
typed in, it runs some logic
in order to process that user
name and handle it appropriately.
Good question.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
So question is what if I wanted
to have multiple variables inside
of this route?
Pretty easy to do.
You can just say if I want to rather
than just saying hello to one person,
say hello to two people, I can do string
name one and slash string name two.
A little bit of a probably not
intuitive user interface for these URLs,
but that's fine.
Name one and name two--
we'll go ahead and
capitalize both of them.
Name two equals name two dot capitalize.
And we'll need to
capitalize name one as well.
And we'll say hello
name one and name two.
And so now I have slash some
string slash them other string.
They're both arguments to this function.
And if I run this again,
go to just slash Julia.
OK, not found.
There is no longer a route that's
just slash some arbitrary string.
The only routes that I have are the
slash Athena and slash two names.
I have to go slash Julia slash Athena.
And then I get OK,
hello Julia and Athena.
Other questions?
Yes?
AUDIENCE: [INAUDIBLE]
BRIAN YU: What about optional arguments?
Great question.
So the situation might arise
in which you don't always
need something to be there.
So maybe by default, if
someone only gave me one name
but didn't give me a second
name, I just always want
to say hi to Josh no matter what.
And so I might come up with a
situation where a particular function
has two routes that can get to it.
I can say OK, maybe I provide
string name one and name two,
or maybe I just provide name one.
Either way, I still
want to run this code.
But if I ran this route here on line 10,
just providing a single name, name one,
then on line 11 when I try
to run the hello function,
name two is not going to have a value.
And so I can give names
two a default value.
I can say all right, name two
by default, is going to be Josh.
And so what happens now?
Now if I just go to Julia, press
Return, I get hello Julia and Josh.
I didn't provide a second
name, so it's automatically
going to fill in Josh
no matter what I type.
I type in slash Krishna.
And it's going to say
hello Krishna and Josh,
automatically putting Josh
there every single time.
And it's not unless I give it a
second name, say Krishna slash Athena,
then now it's going to say
hello Krishna and Athena,
filling in Athena as the
variable for the second name.
So you have this ability to give
a default value to a variable
if you so choose to do so.
And this is not true of just Flask.
You can give default
arguments to functions
anytime you're writing
functions in Python.
And you can find that that
can sometimes be helpful.
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
Does it allow regular expressions?
I don't believe Flask does.
There is a similar framework to Flask
called Django, a little bit heavier
weight, a little bit more
full featured, that does this,
but with a little more sophistication.
And Django does allow for regular
expression-based patterning
based on those routes?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Oh!
Good question.
What's the significance of the
at symbol in at app dot route?
What is it exactly doing here?
This is what we call a Python decorator.
There is an example of the decorators
in the source code examples
that we won't get to today.
But basically what it's doing
is it's taking the function
and then wrapping the function
in some additional logic.
And the logic it's wrapping
it in his logic that says
listen for someone who
goes to this route.
And if you go to the route,
then run this function.
So it's a Python
decorator is what that is.
Other things?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Can the
default variable be none?
Yes, the value of variable can be none.
Name two could be none.
But the situation you'll
get there is, well, we're
going to get an error in this case,
because I go to slash Krishna--
I'm going to get an error because
you can't capitalize the none
type is the error that we'll get.
But yes, you could, in theory,
use none as the default value.
All right, yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Yep.
You'll need to have Flask installed.
But once you have Flask installed, what
you can do via probably PIP3 install
Flask to install Flask, you should be
able to Flask run in the directory that
has application dot pi.
And then that will start
up the web application.
You might need to set the Flask app.
So expert Flask app
equals application dot pi,
if it's not able to find which
Flask app it should be using.
So you might want to try that line
if things aren't quite working.
But a TS can come help you if you're
having trouble with that as well.
All right.
A couple other things I want to talk
about before we get to the project
that we're going to be working on today.
One is this idea of templates.
And so oftentimes, instead of just
returning a string like hello world,
I want to return an actual HTML file.
And so I'm going to import render
template, which is a function.
And rather than returning
hello world, I'm
going to return render
template index dot HTML.
And now on the desktop, I'm
going to make a directory,
a new folder called templates.
And inside of the
templates directory, I'm
going to add a new file
called index dot HTML.
And in index dot HTML
we're going to say HTML--
you can have any arbitrary HTML page.
So now I type Flask run.
I go to the route.
And I get welcome to my website.
Again, I went through
that a little bit quickly.
But basically what I've done
here is I have in application
dot pi render template, rendering
some template to the user.
This file, index dot
HTML is located inside
of a templates directory
that's in the same folder
as application dot by so I have
index dot HTML, which is a template
that I'm loading.
And the nice thing
about these templates is
that I can now add arbitrary logic and
variables to these templates, whereby
I can say if I go to
slash string name, and I
want to say hello to
someone's name, then
I can render index dot HTML
passing in the variable name.
And I want to set that
equal to whatever name is,
but I probably want to
capitalize it first.
So name dot capitalize.
And then in index dot HTML, and I'll
go ahead and show both side by side,
welcome to my website comma, and then in
two curly braces I'm going to say name.
And this is indexed.
You probably remember from CS50 this
is a special templating language called
Ginga that just makes it easy to be
able to template languages in order
to generate HTML files that are dynamic,
that aren't the same every time you
open the page, but that
actually change depending
on the value of variables, for example.
So now if I were to
run this application,
go to slash Josh, for example.
Then I get welcome to my website comma
Josh, where Josh was capitalized.
Reason being, in application
dot pi, I want to slash Josh.
So Josh is the value of
this variable called name.
I'm capitalizing that name, passing
it into the template as something also
called name-- but it could have
been called something different--
and then in the template I'm
saying welcome to my website comma
and then plugging in the name there.
Questions about any of that?
OK.
In that case what I thought we'd
do is try adding some conditions
to this web page now.
So one of my personal
favorite stupid websites
is a website called Is
It Christmas dot com.
Anyone familiar with it?
A couple of people, maybe.
Basically, it's a website that tells
you whether or not it's Christmas.
You go to is it Christmas
dot com press Return.
And this is the page.
So very simple website.
It just says no.
On Christmas it says yes.
And what I thought we'd do is
create a website called is it
New Year's just to get it the idea
OK, how can we actually implement
this sort of logic in
Python and Flask in order
to generate something
somewhat interesting.
And so in Python, there is a
module called date time, which
we're going to end up being using.
And I'll important date
time so that we can use it.
And in Python, if I do something
like date time dot date time dot now,
that's going to give me a value that
represents like, the current moment,
January 20, 2019 that 11: 06
AM, and some number of seconds
and milliseconds.
It's going to give me
all that information.
If I do date time dot date time
dot now dot month, I get just one.
That is the month of
the current time stamp.
And I do dot day, and I get 20,
which is the current day of today.
And so I can use date
time dot date time dot now
to be able to answer
these sorts of questions.
So in application dot pi
now, let's go ahead and say--
well, let me first at the top
of the file import date time,
so we can use the daytime module.
And we'll say now equals date
time dot date time dot now.
And I'll create a new
variable called is New Year's.
And is New Year's is going to be set
to now dot month equals one and now dot
day equals one, meaning if both the
month of what the time stamp is right
now is January is January for
one, and now dot day is also one,
then it is New Year's.
And then I'm going to load
index dot HTML passing in
is New Year's into the template.
I don't need string name anymore.
So I can get rid of that variable.
So I just have this is
New Year's variable now.
And now inside of index.HTML
I can add some logic.
I can say if is New Year's, then--
I'll do H1-- yes else H1 no end if.
So this right here is the Ginga
syntax for creating a condition.
Use these curly braces and percent
symbols say if something, and then
any arbitrary HTML inside of
that, else some other condition.
And then end if to say that's
the end of the if statement.
So we've checked whether
or not it's a new year,
and depending on whether
it is or not, we're
going to get some different output.
And so I run this web
application now, Flask run.
I go to the web page, no
particular route, press Return.
And OK, it says no.
It is not New Year's Day today.
We could cheat a little bit and in
application dot pi just to test it,
we can say is now a day
equal to 20, which is today?
And if I try refreshing
this page, then OK.
Yes, today is January 20.
It's going to show me that as well.
So this sort of logic
of logic we can use
to be able to create conditions
inside of our application
to do different things, depending
on what's happening in the program.
Questions about that so far?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Good question.
So what language is the Ginga code?
The Ginga code is
actually its own language.
Ginga is a templating language.
It's a language that draws a lot
of inspiration from Python though.
So it's got a lot in common with Python.
So a lot of the similar ideas of if and
[INAUDIBLE] if and else from Python.
Those keywords are also
going to be present in Ginga.
So you can do a lot of
those same things in Ginga.
Syntax.
So all right.
What I thought we'd do now
is together begin working
on what's going to be today's project.
And we'll work on this
together for a little bit,
and then I'll set you free to
work on a couple of features.
Then we'll do our lunch break.
We'll come back in the
afternoon for a little more
on Flask and artificial intelligence.
And then we'll continue
working on that project.
And so the project that we're
going to be working on today
is we're going to try and implement
a game of tic-tac-toe online.
And so here is what the
finished application
is going to look like potentially.
Uh-oh, import error.
No module named Flask session.
Let me-- if you ever get
an import error where
there's no module with a particular
name, you can PIP3 install it.
I'll go ahead and install that now.
Flask run.
Great.
Run that application.
Here's what the application
is going to look like.
We're just going to have a
grid, where we can make moves.
This grid is just an HTML table.
And OK, it's X's turn first.
X is going to play somewhere.
O can play somewhere,
X can play somewhere.
O can not play very well.
And if X plays here, then it
says OK, winner of the game
is X. It's a fairly
straightforward game.
You can add additional features.
We'll add a button to reset the game
too, which I'll have you implement.
Play x there, O, X. Maybe o is
not going to be very smart again.
And later this afternoon when we
talk about artificial intelligence
and algorithms like min and max
for being able to play games,
we'll have you, if you'd
like to, try a challenge,
try to implement an artificial
intelligence to play this game so
that you can choose
let computer make move.
Click on that button and
that's going to result
in X playing in a
particular position. it's
going to say winner is X. We're
not going to get there just yet,
but we'll explain the
algorithm later this afternoon
so that if you'd like to try it.
It's something that you
can try but all right.
Let's actually just
start simple and start
trying to create this web application.
So in a new folder, I'm
going to call it tic-tac-toe.
I'm going to create a new file
called application dot pi.
And we're going to say
from Flask import flask.
In order to give everyone a different
game, what we're going to use
is something called Flask session.
You used Flask session when
you were doing CS50 finance.
And you wanted to use
CS50 finance to keep
track of which is the current
user that is logged in,
so that different people logged in and
could see their own different stock
portfolios.
In this case, we're going
to use Flask session
to do the same idea, where
instead of just giving people
a different account,
anyone who's logged in
is going to be able to
play their own game online.
So the session inside
of the session we're
going to store details
about the current game.
So we're going to say
app equals Flask name.
Then we're going to
have a couple of lines
that you might recall from CS50 finance.
So you don't need to
worry too much about,
but they're just some
configuration settings.
We're going to say config session
file der equals make D temp.
Don't worry too much about these lines.
I'll post some source code
examples that you can copy this
from so you don't need to feel like you
need to copy them directly right now.
I'll need to import from
temp file import make D temp.
Basically, if you're curious
what this is doing is
we need some place to actually
store the session data.
The session is data that our
application needs to source somewhere.
And we're just going to
create a temporary directory
in which to store all of
that session information.
We need to say should
the session be permanent?
And we're going to say no,
it shouldn't be permanent.
After some time it's going to expire.
There are fancier ways of trying to set
exactly what that time is going to be.
And then the session type, what
type of session is this going to be?
I'm going to say file system.
In other words, there are different
ways of storing session information.
I'm going to store all the
session information just in files.
You could store them in databases
and cookies and other ways.
Again, I wouldn't worry about any
of these first couple of lines
that I've written.
These are just configuration
to get the application
to work the way we want it to.
Here's where the interesting
stuff starts to happen.
So if I go to the slash route,
we'll call this the index function.
And what I want to do is get
the board from the session.
Like, what I probably
want to do eventually
is store the board, the tic-tac-toe
board, inside of session square bracket
board.
And what might this board look like?
Like, what sort of data
structure do you envision
that we could use to store this board?
Thoughts?
Yeah?
AUDIENCE: [INAUDIBLE]
BRIAN YU: Yeah.
A list of lists.
You might imagine that we
have one list for each row.
And each row is just going to be a
list of three things, either x or zero,
or some value to represent
nothing is here yet,
which might just be the none value.
And so the logic I'm going
to start you off with
is say if a board not in session.
In other words, if there is not
already a board inside of the session,
then I better create the new board.
We'll say section board is equal to--
and right now you need to
create an empty game board.
And then someone suggested maybe
this can be a list of lists.
The first row has nothing in it.
So we'll just say none, none, none.
The second row also has nothing
in it, none, none, none.
The third row also has nothing
in it, none, none, none.
So we've created the board
inside of the session.
What other information do we need to
keep track of with regards to the game?
Whose turn it is.
Great.
And we could probably calculate
that based on the board.
But for simplicity's sake, let's go
ahead and store that in the session
too.
I'll say session square bracket turn.
We'll have X go first.
So the current turn is just
X. They're the player who
is going to be playing first.
And now let me go ahead and just
render a return, render a template.
We'll call it game dot HTML.
And we'll pass in game is going
to be session board, because I
need have access to the board
inside of my HTML template.
And I also need to have
access to whose turn it is.
So turn is going to be session turn.
So inside of game dot
HTML I will have access
to a variable called game, which is
this list of all the current state
of the board, and also a
variable called turn, that
represents whose turn it is right now.
And so I'll go ahead and create
a new folder called templates,
inside of which I'll create a
new file called game dot HTML.
And OK, inside of game dot HTML
I'm going to include some code.
Dot type HTML, HTML head title
is going to be tic-tac-toe.
And inside the body of this application
I'm just going to create a table.
So a table.
And what I'd like to do
is create three rows,
one for each row of the game board.
And so I'm going to do a Ginga loop.
And a loop in Ginga very similar to
what a loop in Python looks like.
I'm going to just say
for I in range three.
I want to do a loop that's going
to run three times, zero, one, two.
What do I need three of?
I need three rows.
TR is the HTML tag for
creating an HTML row.
So I'm going to create a row using TR.
And inside of that row what do I need?
What do I need in each row of
the tic-tac-toe game board?
A TD.
TD stands for table
data or a table cell.
And how many table cells do I need?
Three.
Great.
It's a three by three game board.
So I'm going to have another loop in
here for J in range three and four.
And so inside of each one of these is
going to be a TD, a table data cell.
And for now, I'm just going to say like,
this is a cell, just so I can test this
out, see whether or not it's working.
Oftentimes, a good strategy
before you try to the entire thing
is build a very small
piece of it, just a game
board that is three by three
each filled with this is a cell,
and see if that works.
And then go from there.
So I'll try this, run Flask run.
Go ahead and start up the server.
Named session is not defined.
OK, I think I missed a line somewhere.
Yes, actually missed a couple lines.
First thing I need do is
from Flask I need to import
both render template and session.
And also to enable sessions, I
also need to say session app.
So I added some additional
imports that I needed.
I want to be able to render a template.
I want to be able to access the session.
And to enable Flask session, I
have to say create sessions out
of this web application.
Again, no need to worry
about the specifics of that.
These are lines you can very frequently
copy anytime you need to use them.
So let's try and refresh
that, run it again.
OK.
I have the basics of what is
eventually going to be a game board.
Doesn't look much like a tic-tac-toe
game board at the moment,
but that's OK.
We can add some additional logic
here to be able to help us out here.
And the logic is mostly going
to come from styling code.
So inside the style
section of our website
let's add some CSS that's going
to make this actually look
like a tic-tac-toe board.
So for tables and table
data cells, I wanted
to have a border that is a
one pixel solid block border.
This is basically going to say around
the table and around any data cells,
go in and create a one
pixel border around it.
And so what's that going to do?
It's going to give me
something that looks like this.
All right.
That looks a little bit more like
the game board that I expect.
And actually, I wonder
if I can get rid of table
and just do round table data cells.
All right, if I just go
around table data cells,
then I'll get something
that looks like this.
But what I can do is to make
this look a little better,
I'm going to add styling to the table.
Say border collapse collapse.
And let's see if that works.
Yes, great.
So border collapse is just
a special CSS property
you can add to a table
that says if there
are two borders next to each
other, collapse them down into one.
So without it before, the
game board look something
like this with all the lines
sort of next to each other,
but multiple of them.
If I add in this border
collapse collapse CSS property,
refresh it again.
All the lines sort of come
together, so we actually
get something that looks a little
bit more like a game board.
Questions about that CSS code so far?
All right.
So now what I want to do is I'd
like to make these cells square.
So for each of these table data
cells, I'll give them a width
and say 150 pixels.
I'll give them a
height, also 150 pixels.
And I'll give it a
font size of 30 pixels.
And we'll center it, text align center.
So width and height I'm setting
to the same value, 150 pixels.
So I get some big squares for the game.
I'm setting the font size of what
size the text inside the game board
should be.
30 pixels is what the size of like,
the X and the O is going to be.
And then I'm sending that text,
because I want the X's and O's always
to be centered inside the game board.
So I'll refresh this, and I
get something far too big.
Something that looks like this.
Now, this actually looks like
a game board, but in each one
I've just said this is a set.
So now I'd like to show
something a little bit different,
as opposed to this is the
cell in every single one.
But before I move on, question so far?
All right.
Let's go ahead and try
now, instead of just
saying this is a cell, what we can do
is we can plug in the value of variables
here, variables like
I and J, for instance.
So I can say double Curly
Brace I comma double
Curly Brace J to print out
what the values of I and J
are in any particular case.
And so I'll refresh this page to show
you what that's going to look like.
And so all right.
Now I've printed out
the coordinates of each
of the cells of my
tic-tac-toe game board.
In the upper left I have
rows zero column zero.
Next to that is row zero column one.
After that I have row zero column two.
Then I have row one column zero,
one, and two, and then row two
down there on the bottom.
And so I have all these
cells of the game board.
And now what I'd like to do, instead
of just printing out the cells,
let's print out game
I J. In other words,
print out whatever is stored in the
game board at row I and column J.
And so when I refresh this,
what am I going to see?
Any guesses?
This is what I get, A
board full of nones,
because inside of my array, this game
board array, session Square Bracket
board, I just filled it with a
whole bunch of nothing, none values.
If I were to change one of them and
make it X instead, refresh that.
OK, now it's X, because I'm
able to change the board.
But I'll leave it if none for now.
What I'd like to do is not show
the word none if nothing is there.
If nothing is there, I'd like
to show like, a link that says
like play a move here.
So let me go back.
And so what's the logic here?
I want to show the move
if there is a move.
But if there's no move,
then I want to show
a link that lets you play a move there.
What sort of structure do I need?
Yeah?
AUDIENCE: [INAUDIBLE].
BRIAN YU: Yeah, some sort
of conditional, somewhere I
can say if game I J, in other
words, if there is something
in this spot in the game,
then go ahead and print out
whatever it is there,
else [INAUDIBLE] if we'll
go ahead and add a link, a HREF.
Right now it's not going to go anywhere.
I'll just have it go to pound symbol,
which just takes you to the same page
and say play.
And then whose turn is it?
The current turn is stored
in a variable called turn.
So I can print it out at
this point in the game
by saying play Curly Brace
Curly Brace turn here.
All right.
Play X here.
Play X here.
And now I have that ability.
These links are a little bit big, so
I might want to add some styling code.
Feel free to mess with
the styling on your own.
But I want to say, all right.
For all the links that are
children of table data cells,
that font size should just be
18 point font, for instance.
So those links now are just going to
be 18 point font, instead of 30 point
font.
Refresh that.
OK.
This looks a little bit nicer.
Of course, these buttons don't
actually do anything yet.
I say play X here, nothing happens.
Play X there, nothing happens.
What I'd like to do now is
to have some ability such
that when I click on a
particular cell, it's
going to play a move in
that particular place.
And so how am I going to do that?
Well, in application dot pi I'm
going to add some [INAUDIBLE]..
I'm going to add a route.
I'm going to go after the index route.
I'm going to add a
new route called play.
And this route is going
to be a variable route.
I can go to different routes
based on where on the board
I want to play the next move.
So I might say play int colon row.
Int again, is the type.
Row is just going to be
the name of this variable.
And then slash int
colon call for column.
And I'm just going to call this function
play, which takes a row and a column.
And for now, I'm just going to return--
right now I'm just going
to return play to move.
It's not going to do anything yet.
It's just going to say OK, you played
a move, even though you haven't yet.
And so what this is
going to allow me to do
is if I go to slash play
slash one slash one,
then it's going to play a
move in row one column one,
or at least that's the goal.
So now if I go back here
unexpected [INAUDIBLE]??
Did I not save?
Better?
I see the game board.
I click on a place to play.
OK, nothing happened just yet.
Why?
Because I didn't actually say these
links should go to that route.
So let's the next thing I need to do.
Inside of game dot
HTML, these links I want
to go to this play function
passing in some value for the row
and for the column.
There is some special syntax you can
use here that we didn't see in CS50,
but that's actually quite powerful,
a function called URL for.
It basically generates the
URL based on the values
that you want to plug
into the particular spots.
And so I'll show you the code
here, just so you've seen it once.
We're going to go Curly Brace Curly
Brace to mean plug in something here.
And I'm going to plug in URL for.
And the argument to URL for
is the name of the function
that I want to bring the user to.
And the function I want to
bring the user to in this case
is just called play.
So I'm going to redirect
to URL for play.
But play as a function
takes two arguments.
It takes row and it takes call.
So URL for also lets me specify
what should the value of row be,
and what should the value of call be?
And so what should the value
of row be in this case?
I, OK.
And the value of call?
J. All right, great.
So I'm redirecting the
play row I column J.
Let's give this a try.
Refresh the page,
nothing seems to happen.
But if I try and play in
the upper left for example,
what it takes me to is
up there in the URL bar
you see slash play slash zero
slash zero, row zero column zero.
And it says, all right,
you played a move.
I've been taken to a route that's
dependent upon the row and the column
where by now I can
actually play that move.
Right now of course, it doesn't do that.
After I play a move, I'm probably going
to run some logic inside of this play
function that updates sessions
square bracket board and also
updates session square bracket turn.
And then at the end, I probably want to
redirect myself back to the index page.
And I can say URL for index
to say redirect myself back
to the index function.
I'll need to import both
redirect and URL for in to Flask.
But now if I run this application,
I see an empty game board.
I press the place where
I want to make a move.
And it takes me, redirects me
back to the same game board.
And what you want to be able
to do is have the move actually
appear there by updating the board.
And so before I move on,
questions about anything
we've done so far, about the goals,
about why we did things the way we did?
Anything about that?
All right.
Then what I think we'll do now is in a
moment after I get some instructions,
we'll break out into smaller groups,
and we'll actually try working on this.
I'll post this starter
source code on the website,
if you haven't been following
along quite exactly.
But first step is get the
application working up to this point,
just so you can display the board,
see places where you can play moves.
But the big step is going to be to
implement this play function now,
this idea of OK, if I want to
play in this row in this column,
update the board,
change whose turn it is,
and then redirect back
to the index page.
And then if you manage to finish
that before the hour is up,
the next feature to implement is
the idea of a reset game button.
Add a button to the bottom
of the web page that
just says reset game,
whereby if you click it,
it resets the game back
to its original state.
And so think about how you might
want to go about implementing that.
We'll work on that for
about an hour or so.
We'll break for lunch at 12:30.
We'll reconvene back here at 2 PM
where we'll talk a little bit more
about Flask, talk about
artificial intelligence
and how to program
artificial intelligence.
And then we'll spend the rest
of the afternoon continuing
to work on this project.
We'll go ahead and break
up into groups again.
And which group has not been
here in the auditorium yet?
I think this group.
So this group will stay
here in the auditorium.
We'll have you all go to room 136.
And you all go to room 212.
And the teaching fellows will spread
out and help you there as well.
I'll post the source code online
so you can begin working on that.
And we'll go until 12:30.
