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.
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.