WEBVTT X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000 00:00:00.984 --> 00:00:17.787 [MUSIC PLAYING] 00:00:17.787 --> 00:00:19.620 BRIAN YU: All right, welcome back, everyone, 00:00:19.620 --> 00:00:21.930 to Web Programming with Python and JavaScript. 00:00:21.930 --> 00:00:24.690 And today, we take a look at one of the two main languages we're 00:00:24.690 --> 00:00:26.310 going to be looking at in this course. 00:00:26.310 --> 00:00:28.710 In particular, we're going to be looking at Python. 00:00:28.710 --> 00:00:30.870 Python is a very powerful language that makes 00:00:30.870 --> 00:00:33.438 it very easy to build applications quickly 00:00:33.438 --> 00:00:36.480 because there are a lot of features that are built into the language that 00:00:36.480 --> 00:00:39.420 just make it convenient for quick and productive development. 00:00:39.420 --> 00:00:42.990 So one of the goals of today is to introduce you to the Python programming 00:00:42.990 --> 00:00:44.860 language if you haven't seen it before. 00:00:44.860 --> 00:00:46.980 And even if you have seen it before, to give you 00:00:46.980 --> 00:00:49.578 a taste for what the language has to offer, exploring some 00:00:49.578 --> 00:00:51.870 of the more advanced features in some of the techniques 00:00:51.870 --> 00:00:55.410 we can use using Python to be able to develop applications 00:00:55.410 --> 00:00:57.030 all the more effectively. 00:00:57.030 --> 00:01:00.480 So we begin with our very first Python program, just a program 00:01:00.480 --> 00:01:01.800 that says, "hello, world." 00:01:01.800 --> 00:01:03.810 We're going to be writing it in a text file. 00:01:03.810 --> 00:01:07.140 And the program just looks like a single line, just like this. 00:01:07.140 --> 00:01:09.450 And if you've used other programming languages before, 00:01:09.450 --> 00:01:12.060 like C, or Java, or other languages, this probably 00:01:12.060 --> 00:01:14.290 looks pretty familiar syntax-wise. 00:01:14.290 --> 00:01:16.260 But just to break it down, we have a function 00:01:16.260 --> 00:01:19.740 called Print built into the Python programming language for us. 00:01:19.740 --> 00:01:23.250 And like many other programming languages, functions in Python 00:01:23.250 --> 00:01:25.680 take their arguments inside of parentheses. 00:01:25.680 --> 00:01:29.280 So inside of these parentheses are the argument, or the input, 00:01:29.280 --> 00:01:32.280 to the Print function, which in this case is just the words, 00:01:32.280 --> 00:01:35.740 "hello, world" followed by an exclamation point. 00:01:35.740 --> 00:01:38.760 So here's how we can actually take this program and run it. 00:01:38.760 --> 00:01:40.770 I'm going to go into my text editor and create 00:01:40.770 --> 00:01:44.820 a new file that I'll call hello.py. 00:01:44.820 --> 00:01:49.830 dot-py or dot-py is the conventional extension for Python programs. 00:01:49.830 --> 00:01:53.880 So I create a file called hello.pi inside of which will just be the Python 00:01:53.880 --> 00:01:56.070 code that we just saw a moment ago. 00:01:56.070 --> 00:01:57.700 We'll call the Print function. 00:01:57.700 --> 00:02:00.570 And as an argument, or the input, to the Print function, 00:02:00.570 --> 00:02:05.360 I'll say, "hello, world" exclamation point. 00:02:05.360 --> 00:02:09.770 Now in order to run this program, we're going to use a program in our terminal 00:02:09.770 --> 00:02:12.680 that also just so happens to be called Python. 00:02:12.680 --> 00:02:15.380 Python is what you might call an interpreted language, 00:02:15.380 --> 00:02:17.900 meaning we're going to run a program called Python, 00:02:17.900 --> 00:02:22.070 which is an interpreter that is going to read our .py file line by line, 00:02:22.070 --> 00:02:25.640 executing each line and interpreting what it is that it means in a way that 00:02:25.640 --> 00:02:27.710 the computer can actually understand. 00:02:27.710 --> 00:02:30.320 So we'll run Python followed by the name of the program 00:02:30.320 --> 00:02:33.480 that we'd like to interpret, in this case hello.py. 00:02:33.480 --> 00:02:36.710 And when we run this program, we see that the words "hello, world" 00:02:36.710 --> 00:02:38.120 are printed to the terminal. 00:02:38.120 --> 00:02:38.970 And that's it. 00:02:38.970 --> 00:02:40.220 That's the end of the program. 00:02:40.220 --> 00:02:42.530 And that's the very first program that we've written 00:02:42.530 --> 00:02:44.780 using the Python programming language. 00:02:44.780 --> 00:02:47.240 So now already we've seen a couple of features of Python. 00:02:47.240 --> 00:02:49.160 The ability to interpret Python-- there's 00:02:49.160 --> 00:02:53.150 no need to compile it into a binary first in order to run a Python program. 00:02:53.150 --> 00:02:54.350 We've seen functions. 00:02:54.350 --> 00:02:58.730 And we've also seen strings, just text that we can provide in quotation marks 00:02:58.730 --> 00:03:02.930 that we can provide as input to other functions or manipulate in other ways. 00:03:02.930 --> 00:03:07.130 And we'll see some examples of a string manipulation a little bit later. 00:03:07.130 --> 00:03:11.150 Like many other programming languages, Python also supports variables. 00:03:11.150 --> 00:03:13.700 And in order to assign a new value to a variable, 00:03:13.700 --> 00:03:15.870 the syntax looks a little something like this. 00:03:15.870 --> 00:03:19.160 If I have a line like a equals 28, what that's going to mean 00:03:19.160 --> 00:03:22.880 is take the value 28 and assign it, store it inside 00:03:22.880 --> 00:03:25.130 of this variable called a. 00:03:25.130 --> 00:03:27.448 Now, unlike other languages like C or Java 00:03:27.448 --> 00:03:29.240 which you might be familiar with, where you 00:03:29.240 --> 00:03:31.250 have to specify the type of every variable 00:03:31.250 --> 00:03:35.210 you create-- you have to say, like, int a to mean a is an integer. 00:03:35.210 --> 00:03:38.360 Python doesn't require you to tell you what the types of each 00:03:38.360 --> 00:03:40.400 of these variables actually are. 00:03:40.400 --> 00:03:42.620 So we can just say a equals 28 and Python 00:03:42.620 --> 00:03:45.350 knows that because this number is an int, that it's 00:03:45.350 --> 00:03:49.130 going to represent the variable a as an int, that it knows, 00:03:49.130 --> 00:03:53.900 it's able to infer, what the types of any these values happen to be. 00:03:53.900 --> 00:03:56.180 So all the values do indeed have types. 00:03:56.180 --> 00:03:58.650 You just don't explicitly need to state them. 00:03:58.650 --> 00:04:01.400 So for example, this here, the number 28, is a type int. 00:04:01.400 --> 00:04:02.390 It's an integer. 00:04:02.390 --> 00:04:04.880 A number like 1.5 has a decimal in it. 00:04:04.880 --> 00:04:06.440 It's a floating point number. 00:04:06.440 --> 00:04:09.440 So that, in Python, is what we might call a float type. 00:04:09.440 --> 00:04:11.450 Any type of text, something like the word 00:04:11.450 --> 00:04:14.580 "hello" wrapped in either double quotation marks or single quotation 00:04:14.580 --> 00:04:15.080 marks-- 00:04:15.080 --> 00:04:19.910 Python supports both-- is what we would call the str type, short for string. 00:04:19.910 --> 00:04:21.890 We also have a type for Boolean values, things 00:04:21.890 --> 00:04:24.350 that can be either true or false. 00:04:24.350 --> 00:04:28.940 In Python, those are represented using a capital T, true, and a capital F, 00:04:28.940 --> 00:04:29.850 false. 00:04:29.850 --> 00:04:31.490 Those are of type bool. 00:04:31.490 --> 00:04:33.800 And also, we have a special type in Python 00:04:33.800 --> 00:04:38.720 called the none type, which only has one possible value, this capital N, none. 00:04:38.720 --> 00:04:41.030 And none as a value we'll use whenever we 00:04:41.030 --> 00:04:43.790 want to represent the lack of a value somewhere. 00:04:43.790 --> 00:04:46.490 So if we have a function that is not returning anything, 00:04:46.490 --> 00:04:48.950 it is really returning none, effectively. 00:04:48.950 --> 00:04:51.140 And so you might imagine that none can be useful 00:04:51.140 --> 00:04:55.282 if ever you want a variable to represent the absence of something, for example. 00:04:55.282 --> 00:04:58.490 So lots of different possible types, and there are more types than just this. 00:04:58.490 --> 00:05:01.940 But here's a sampling of the possible variables and types that 00:05:01.940 --> 00:05:04.820 might exist inside of this language. 00:05:04.820 --> 00:05:07.670 So now let's try and actually use a variable 00:05:07.670 --> 00:05:11.270 in order to do something a little bit more interesting inside of our program. 00:05:11.270 --> 00:05:14.780 And we'll write a program that's able to take input from the user 00:05:14.780 --> 00:05:17.540 in order to say hello to them, for example. 00:05:17.540 --> 00:05:19.620 So I'll create a new file. 00:05:19.620 --> 00:05:22.274 We'll call it name.py. 00:05:22.274 --> 00:05:25.200 And to start, I'd like to prompt the user for input. 00:05:25.200 --> 00:05:29.590 I'd like to prompt the user to, for example, type in their name. 00:05:29.590 --> 00:05:30.820 So how might we do that? 00:05:30.820 --> 00:05:33.090 Well, just as there is a Print function that 00:05:33.090 --> 00:05:36.690 is built into Python that just prints out whatever the argument happens 00:05:36.690 --> 00:05:39.510 to be, Python also has a built in function 00:05:39.510 --> 00:05:42.690 called Input that prompts the user for input and asks them just 00:05:42.690 --> 00:05:44.470 type in some input. 00:05:44.470 --> 00:05:48.810 So let's provide some input and ask the user to type in their name, 00:05:48.810 --> 00:05:50.380 for example. 00:05:50.380 --> 00:05:52.860 And then we can save the result, the output 00:05:52.860 --> 00:05:55.085 of that function inside of a variable. 00:05:55.085 --> 00:05:57.960 And in this case, I'll save it inside of a variable that in this case 00:05:57.960 --> 00:06:01.030 also just happens to be called Name. 00:06:01.030 --> 00:06:02.500 Now we can run the program. 00:06:02.500 --> 00:06:07.740 I can run the program by going into my terminal and typing Python name.py. 00:06:07.740 --> 00:06:08.560 I'll press Return. 00:06:08.560 --> 00:06:11.230 And we'll see the program prompts me to type in my name. 00:06:11.230 --> 00:06:15.490 I see name colon space, which is that string I provided as the argument 00:06:15.490 --> 00:06:16.960 to the input function. 00:06:16.960 --> 00:06:19.960 And this now prompts me to type in my name, so I will. 00:06:19.960 --> 00:06:23.120 And after that, nothing seems to happen so far. 00:06:23.120 --> 00:06:25.120 So now I'd like to do something with that input. 00:06:25.120 --> 00:06:26.200 I typed in my name. 00:06:26.200 --> 00:06:29.820 I'd like to say, like, hello, for example to myself. 00:06:29.820 --> 00:06:31.630 So I'll go back into this program. 00:06:31.630 --> 00:06:36.640 And now what I can do is I can say print hello comma. 00:06:36.640 --> 00:06:39.820 And then I can say plus name. 00:06:39.820 --> 00:06:42.620 This plus operator in Python does a number of different things. 00:06:42.620 --> 00:06:45.290 If I have two numbers, it'll add those two numbers together. 00:06:45.290 --> 00:06:49.180 But with two strings, plus can actually concatenate, or combine, 00:06:49.180 --> 00:06:50.600 two strings together. 00:06:50.600 --> 00:06:54.610 So I can combine hello comma space with whatever the value of name 00:06:54.610 --> 00:06:56.290 happens to be. 00:06:56.290 --> 00:06:58.510 So now I'll rerun this program. 00:06:58.510 --> 00:07:01.492 Python name.py. 00:07:01.492 --> 00:07:03.270 Type in my name. 00:07:03.270 --> 00:07:07.800 And now we see "hello, Brian" as the output of the program. 00:07:07.800 --> 00:07:11.960 So this is one way that you can manipulate strings in Python. 00:07:11.960 --> 00:07:15.230 Another way that's quite popular in later versions of Python 3 00:07:15.230 --> 00:07:19.090 is a method known as using f strings, short for formatted strings. 00:07:19.090 --> 00:07:21.260 And in order to use f strings in Python, it's 00:07:21.260 --> 00:07:24.230 going to be a similar but slightly different syntax. 00:07:24.230 --> 00:07:27.770 Instead of just having a string in double quotation marks, 00:07:27.770 --> 00:07:31.010 we'll put the letter f before the string. 00:07:31.010 --> 00:07:34.730 And inside of the string, I can now say hello comma-- 00:07:34.730 --> 00:07:36.650 and then if in a formatted string, if I want 00:07:36.650 --> 00:07:39.620 to plug in the value of a variable, I can do so 00:07:39.620 --> 00:07:42.270 by specifying it in curly braces. 00:07:42.270 --> 00:07:47.360 So what I'll say here is, inside of curly braces, name. 00:07:47.360 --> 00:07:50.720 And so what's going on here is I am telling this formatted string 00:07:50.720 --> 00:07:54.060 to substitute right here the value of a variable. 00:07:54.060 --> 00:07:56.400 So I prompted the user for input to type in their name. 00:07:56.400 --> 00:08:00.620 We took their name, saved it inside of this variable called name. 00:08:00.620 --> 00:08:03.080 And now here in this print statement on line two, 00:08:03.080 --> 00:08:06.920 I'm printing out a formatted string that is hello comma. 00:08:06.920 --> 00:08:09.530 And then in curly braces here, I'm saying plug 00:08:09.530 --> 00:08:12.078 in the value of the variable name. 00:08:12.078 --> 00:08:14.870 And so that is going to have the effect of taking whatever name was 00:08:14.870 --> 00:08:17.240 provided as input and printing it out. 00:08:17.240 --> 00:08:19.340 And this is a slightly more efficient way 00:08:19.340 --> 00:08:22.160 of being able to quickly create strings by plugging 00:08:22.160 --> 00:08:24.370 in values into those strings. 00:08:24.370 --> 00:08:29.620 So now I'll see the exact same behavior if I run Python name.py 00:08:29.620 --> 00:08:31.830 and prompt it to type in my name. 00:08:31.830 --> 00:08:32.960 I'm like, Brian. 00:08:32.960 --> 00:08:35.812 And then I see the result, "hello, Brian" for example. 00:08:35.812 --> 00:08:37.520 And so those are a couple of ways that we 00:08:37.520 --> 00:08:41.210 can deal with strings, manipulating strings, and combining strings 00:08:41.210 --> 00:08:44.310 using this technique. 00:08:44.310 --> 00:08:47.720 So in addition to variables, Python also supports all of the same other features 00:08:47.720 --> 00:08:50.390 that are core to many procedural programming languages, 00:08:50.390 --> 00:08:52.650 such as conditions, for example. 00:08:52.650 --> 00:08:57.290 So let's take a look at an example now of seeing whether a number is positive, 00:08:57.290 --> 00:09:00.500 or negative, or zero, for example. 00:09:00.500 --> 00:09:04.580 So I'll create a new file that I'll call conditions.py. 00:09:04.580 --> 00:09:09.270 And inside of conditions.py, I'll first prompt the user to type in some input. 00:09:09.270 --> 00:09:14.310 I'll say input number to mean type in a number. 00:09:14.310 --> 00:09:19.700 And we'll save that input inside of a variable that I'm just going to call n. 00:09:19.700 --> 00:09:21.620 And now I can ask questions. 00:09:21.620 --> 00:09:26.000 I can say something like if n is greater than zero. 00:09:26.000 --> 00:09:30.580 Then print n is positive. 00:09:30.580 --> 00:09:33.480 And so what's going on here is I have a Python condition. 00:09:33.480 --> 00:09:35.400 And the way a Python condition works is it 00:09:35.400 --> 00:09:39.930 begins with this keyword, a keyword like if, followed by a Boolean 00:09:39.930 --> 00:09:43.320 expression, some expression that's going to evaluate to true, or false, 00:09:43.320 --> 00:09:45.270 or something kind of like true or false. 00:09:45.270 --> 00:09:48.060 We can be a little bit loose about that, as we may see later. 00:09:48.060 --> 00:09:51.240 And then a colon means, all right, here is the beginning 00:09:51.240 --> 00:09:53.280 of the body of the if statement. 00:09:53.280 --> 00:09:55.140 And in Python, the way we know that we're 00:09:55.140 --> 00:09:58.380 inside the body of an if statement or inside the body of any other block 00:09:58.380 --> 00:10:00.760 of code is via indentation. 00:10:00.760 --> 00:10:03.990 So in some languages, like C, or in languages like HTML, 00:10:03.990 --> 00:10:07.470 which we saw a couple of lectures ago, the indentation 00:10:07.470 --> 00:10:11.250 isn't strictly required by the computer to be able to parse and understand 00:10:11.250 --> 00:10:12.540 what's inside the program. 00:10:12.540 --> 00:10:14.130 In Python, it's different. 00:10:14.130 --> 00:10:17.370 The indentation is required because the indentation 00:10:17.370 --> 00:10:21.360 is how the program knows what code is inside of the if statement 00:10:21.360 --> 00:10:24.610 and what code is outside of the if statement. 00:10:24.610 --> 00:10:26.580 So we have if n is greater than zero colon. 00:10:26.580 --> 00:10:29.340 And then everything indented underneath the if, 00:10:29.340 --> 00:10:31.390 is all of the body of the if statement. 00:10:31.390 --> 00:10:33.420 It is the lines of code that will execute 00:10:33.420 --> 00:10:37.260 if this condition, this Boolean expression and greater than zero, 00:10:37.260 --> 00:10:38.650 happens to be true. 00:10:38.650 --> 00:10:42.900 So if end is greater than zero, will print out n is positive. 00:10:42.900 --> 00:10:45.810 And then we can add an additional condition. 00:10:45.810 --> 00:10:47.680 I can say something like-- 00:10:47.680 --> 00:10:52.757 well, I could say something like else print n is not positive. 00:10:52.757 --> 00:10:54.840 But I can be a little bit more specific than that. 00:10:54.840 --> 00:10:56.400 Here is a sort of two branches. 00:10:56.400 --> 00:10:59.400 One if n is greater than 0, and one else case 00:10:59.400 --> 00:11:02.070 to handle all of the other possible scenarios. 00:11:02.070 --> 00:11:05.220 But really, what I'd like to do is perform a second check. 00:11:05.220 --> 00:11:07.590 In other languages, this might be called an else-if. 00:11:07.590 --> 00:11:11.820 Like, if this condition is not true but this other condition is true. 00:11:11.820 --> 00:11:14.420 Python abbreviated this to just elif-- 00:11:14.420 --> 00:11:17.680 E-L-I-F, just short for else-if. 00:11:17.680 --> 00:11:23.760 So I can say elif n is less than zero, then let's go ahead and print out n 00:11:23.760 --> 00:11:30.920 is negative, and else print n is zero, 00:11:30.920 --> 00:11:33.800 So the idea here now is that if n is greater than zero, 00:11:33.800 --> 00:11:35.960 we perform some task elif-- 00:11:35.960 --> 00:11:38.060 in other words, if it's not greater than zero-- 00:11:38.060 --> 00:11:40.490 then we check to see if it is less than zero. 00:11:40.490 --> 00:11:42.950 In which case, we print out that n as negative. 00:11:42.950 --> 00:11:45.920 Else, if neither of those two conditions are true, it's not positive 00:11:45.920 --> 00:11:48.410 and it's not negative, the only remaining possibility 00:11:48.410 --> 00:11:49.700 is that n is zero. 00:11:49.700 --> 00:11:52.210 So we can print out that n zero. 00:11:52.210 --> 00:11:54.140 And so we might like for this program to work. 00:11:54.140 --> 00:11:57.520 But watch what happens if I now try and run conditions.py. 00:11:57.520 --> 00:12:00.080 Even though logically in our heads and looking at it now, 00:12:00.080 --> 00:12:05.150 it probably seems pretty logical, if I run Python conditions.py and type 00:12:05.150 --> 00:12:05.900 in a number-- 00:12:05.900 --> 00:12:10.080 I'll type in the number five, for example, just see what happens. 00:12:10.080 --> 00:12:12.140 All right, something weird just happened. 00:12:12.140 --> 00:12:14.720 And this is our very first Python exception, 00:12:14.720 --> 00:12:17.210 an error that happens because something didn't quite 00:12:17.210 --> 00:12:19.610 go right inside of our Python program. 00:12:19.610 --> 00:12:22.460 And over time, you'll begin to learn how to parse this exception, 00:12:22.460 --> 00:12:25.190 and understand what it means, and where to begin to debug. 00:12:25.190 --> 00:12:26.900 But learning how to read these exceptions 00:12:26.900 --> 00:12:29.000 and figure out how to deal with them is definitely 00:12:29.000 --> 00:12:32.530 a very valuable skill on your way to becoming a Python developer. 00:12:32.530 --> 00:12:35.600 And so let's see if we can figure out what this exception to saying. 00:12:35.600 --> 00:12:37.970 Oftentimes, I start by looking at the bottom. 00:12:37.970 --> 00:12:39.863 I see that there is a type error. 00:12:39.863 --> 00:12:42.030 That is the type of the exception that has happened. 00:12:42.030 --> 00:12:44.738 There are a lot of exceptions that can go wrong in Python, things 00:12:44.738 --> 00:12:46.640 that we can do that cause errors. 00:12:46.640 --> 00:12:48.890 In this case, it's a type error, which generally 00:12:48.890 --> 00:12:52.040 means that there's some mismatch of types, 00:12:52.040 --> 00:12:54.980 that Python expected something to be of one type 00:12:54.980 --> 00:12:57.552 but it turned out to be a different type. 00:12:57.552 --> 00:12:59.510 So let's try and understand what this might be. 00:12:59.510 --> 00:13:04.670 It says greater than sine, not supported between instances of stir, 00:13:04.670 --> 00:13:07.760 short for string, and int. 00:13:07.760 --> 00:13:09.085 So what does that mean? 00:13:09.085 --> 00:13:11.210 Well, I guess it means that the greater than symbol 00:13:11.210 --> 00:13:13.490 that checks if one thing is greater than another 00:13:13.490 --> 00:13:17.290 doesn't work if you're comparing a string to an integer. 00:13:17.290 --> 00:13:19.040 And that's probably pretty reasonable. 00:13:19.040 --> 00:13:22.063 It doesn't really make sense to say a string is greater than or less 00:13:22.063 --> 00:13:22.730 than an integer. 00:13:22.730 --> 00:13:24.680 When we're talking about greater than or less than, 00:13:24.680 --> 00:13:26.210 usually we're talking about numbers. 00:13:26.210 --> 00:13:29.100 So they should both be integers, for example. 00:13:29.100 --> 00:13:32.790 So why do we think that greater than is comparing a string and an integer? 00:13:32.790 --> 00:13:34.790 Well, now we can look a little bit further up 00:13:34.790 --> 00:13:38.180 at the trace-back, which will show which parts of the code 00:13:38.180 --> 00:13:39.568 are really causing this problem. 00:13:39.568 --> 00:13:41.610 And in this case, the trace-back is pretty short. 00:13:41.610 --> 00:13:44.800 It's just pointing me to a single line of a single file. 00:13:44.800 --> 00:13:49.160 It's saying in the file conditions.py on line three, 00:13:49.160 --> 00:13:54.890 here is the line that triggered the exception-- if n is greater than zero. 00:13:54.890 --> 00:13:56.600 So, what's the exception here? 00:13:56.600 --> 00:13:59.910 Well, zero is obviously an integer because that just is an integer. 00:13:59.910 --> 00:14:03.200 And so if greater than thinks that it's comparing a string with an integer, 00:14:03.200 --> 00:14:05.570 then n somehow must be a string. 00:14:05.570 --> 00:14:10.310 Even though I typed in the number five, it must still think n is a string. 00:14:10.310 --> 00:14:11.278 So why might that be? 00:14:11.278 --> 00:14:13.070 Let's take a look at the code again and see 00:14:13.070 --> 00:14:15.230 if we can figure out what's going on. 00:14:15.230 --> 00:14:19.700 Well, it seems that this input function doesn't care what you type in. 00:14:19.700 --> 00:14:22.490 It's always going to give you back a string. 00:14:22.490 --> 00:14:24.655 n somehow is ending up as a string, which 00:14:24.655 --> 00:14:26.780 is pretty reasonable because the input function has 00:14:26.780 --> 00:14:30.120 no idea whether I typed in a number, or whether I typed in a letter, 00:14:30.120 --> 00:14:32.150 or I typed in other characters altogether. 00:14:32.150 --> 00:14:36.380 So input doesn't know to give back its data in the form of an int, 00:14:36.380 --> 00:14:38.540 or in the form of a float, or in any other form. 00:14:38.540 --> 00:14:40.820 So by default, it's just going to return a string. 00:14:40.820 --> 00:14:45.737 What characters did the user type in as their input? 00:14:45.737 --> 00:14:48.320 So what I'd like to do now, in order to make this program work 00:14:48.320 --> 00:14:51.980 the way I want it to, is take this and convert it into an integer, 00:14:51.980 --> 00:14:56.030 or cast it into an integer, so to speak. 00:14:56.030 --> 00:14:59.630 And the way that I can do that is by using a function in Python 00:14:59.630 --> 00:15:04.050 called int, that takes anything and turns it into an integer. 00:15:04.050 --> 00:15:06.440 So here, I can say int-- 00:15:06.440 --> 00:15:09.590 and then as the argument to the int function, the input to the int 00:15:09.590 --> 00:15:14.602 function, I'm just going to include this whole expression, input number. 00:15:14.602 --> 00:15:16.560 So I'm going to ask the user to input a number. 00:15:16.560 --> 00:15:18.130 They type in some text. 00:15:18.130 --> 00:15:20.970 The input function gives me back a string. 00:15:20.970 --> 00:15:25.700 And that string is going to serve as the input to the int function, which 00:15:25.700 --> 00:15:30.070 then gets saved inside of this variable called n. 00:15:30.070 --> 00:15:32.800 So now that we know that n is indeed an integer, 00:15:32.800 --> 00:15:34.760 let's try and run this program again. 00:15:34.760 --> 00:15:38.710 I'll go back into the terminal, run Python, conditions.py, 00:15:38.710 --> 00:15:40.390 I'm asked to type in a number. 00:15:40.390 --> 00:15:42.430 I type in a number like five. 00:15:42.430 --> 00:15:45.897 And all right, that still doesn't seem to have worked. 00:15:45.897 --> 00:15:47.980 And it didn't work because I didn't save the file. 00:15:47.980 --> 00:15:51.580 So I'll go ahead and save the file, try it again, type in a number. 00:15:51.580 --> 00:15:53.950 And now we see that indeed, n is positive. 00:15:53.950 --> 00:15:55.360 We get no more exception. 00:15:55.360 --> 00:15:57.430 We were able to run the code successfully and see 00:15:57.430 --> 00:15:59.350 that the value of n is positive. 00:15:59.350 --> 00:16:02.440 And I could try this again to test the other conditional branches. 00:16:02.440 --> 00:16:06.480 Type in negative one for example to see that n is negative. 00:16:06.480 --> 00:16:09.340 And otherwise if it isn't either positive or negative, 00:16:09.340 --> 00:16:12.190 then we know that n is zero. 00:16:12.190 --> 00:16:15.310 And so here was our first exposure to conditions in Python, 00:16:15.310 --> 00:16:17.530 the ability to have multiple different branches 00:16:17.530 --> 00:16:20.710 and do different code depending on some expression 00:16:20.710 --> 00:16:22.510 that we're going to evaluate to either be 00:16:22.510 --> 00:16:26.440 a true expression or a false expression. 00:16:26.440 --> 00:16:29.190 All right, so let's take a look at some of the other features that 00:16:29.190 --> 00:16:31.710 are going to be present inside the Python language. 00:16:31.710 --> 00:16:34.080 And one of the most powerful features of Python 00:16:34.080 --> 00:16:36.510 are its various different types of sequences, 00:16:36.510 --> 00:16:39.600 data types that store values in some sort of sequence 00:16:39.600 --> 00:16:42.300 or some collection of values altogether. 00:16:42.300 --> 00:16:47.050 So I go ahead and create a new file that we'll call sequences.py. 00:16:47.050 --> 00:16:49.300 And there are a number of different types of sequences 00:16:49.300 --> 00:16:51.400 that all obey similar properties. 00:16:51.400 --> 00:16:54.620 But one of the types of sequences is the type we've already seen, 00:16:54.620 --> 00:16:56.990 which is just a string, for example. 00:16:56.990 --> 00:17:01.780 So if I have a name, and the name is something like Harry, for instance, 00:17:01.780 --> 00:17:07.240 and this sequence allows me to access individual elements inside 00:17:07.240 --> 00:17:08.460 of the sequence. 00:17:08.460 --> 00:17:11.650 And in order to do so, it's much like an array in other languages, 00:17:11.650 --> 00:17:13.510 if you've experienced them before. 00:17:13.510 --> 00:17:19.270 But I can print out name square bracket zero. 00:17:19.270 --> 00:17:21.550 This square bracket notation takes a sequence, 00:17:21.550 --> 00:17:24.310 some ordered sequence of elements, and gets 00:17:24.310 --> 00:17:28.339 me access to one particular element inside of that sequence. 00:17:28.339 --> 00:17:32.230 And so if I have a string-like name and I say name square bracket zero, 00:17:32.230 --> 00:17:35.320 the effect of that is going to be take this long sequence 00:17:35.320 --> 00:17:37.600 and get me the zero-th element. 00:17:37.600 --> 00:17:40.570 In many programming languages and in programming more generally, 00:17:40.570 --> 00:17:42.820 we often start counting things at zero. 00:17:42.820 --> 00:17:44.560 So the very first item in the sequence is 00:17:44.560 --> 00:17:47.200 item zero, the second item is item one. 00:17:47.200 --> 00:17:49.570 So it's easy to get slight off by one errors there. 00:17:49.570 --> 00:17:53.050 But just know that item zero of the name should be the first character 00:17:53.050 --> 00:17:54.150 in the name. 00:17:54.150 --> 00:17:57.250 And I can see that for sure, if I run Python-- 00:17:57.250 --> 00:18:00.822 I'll save this file, run Python sequences.py. 00:18:00.822 --> 00:18:04.510 And what I get is just the first character of Harry's name, which 00:18:04.510 --> 00:18:06.490 in this case is the letter H. 00:18:06.490 --> 00:18:09.550 If I instead asked to print out character one, which 00:18:09.550 --> 00:18:13.270 would be the second character in the name, if we run the program, 00:18:13.270 --> 00:18:15.610 now I get the letter a. 00:18:15.610 --> 00:18:18.760 And this type of indexing works for many different types of sequences, 00:18:18.760 --> 00:18:22.310 not just a string, which so happens to be a sequence of characters, 00:18:22.310 --> 00:18:23.650 but other types as well. 00:18:23.650 --> 00:18:27.020 Python, for example, has a type for lists of data. 00:18:27.020 --> 00:18:30.520 So if I have a sequence of any type of data that I want to store, 00:18:30.520 --> 00:18:34.480 I can store that information inside of a list in Python. 00:18:34.480 --> 00:18:36.700 So maybe instead of storing one name, I have 00:18:36.700 --> 00:18:38.680 multiple names that I want to store. 00:18:38.680 --> 00:18:44.160 So I want to store names like Harry, and Ron, and Hermione, for example. 00:18:44.160 --> 00:18:49.000 So now I have three names all stored in the sequence inside of a Python list. 00:18:49.000 --> 00:18:51.490 And I can-- you know, I can print out all of the names, 00:18:51.490 --> 00:18:53.740 for example, just to print out all of the names 00:18:53.740 --> 00:18:56.800 to see what the value of the variable names is equal to. 00:18:56.800 --> 00:18:59.170 And we'll see that when I do that, I get a printout 00:18:59.170 --> 00:19:00.430 of the contents of that list. 00:19:00.430 --> 00:19:03.470 Harry, Ron, Hermione, in that particular order. 00:19:03.470 --> 00:19:07.960 But you could also, much as you could index into a string, index into a list 00:19:07.960 --> 00:19:10.960 to say get me just the very first item inside 00:19:10.960 --> 00:19:14.790 of this names list, which in this case, when I run the program, 00:19:14.790 --> 00:19:16.348 is going to just be Harry. 00:19:16.348 --> 00:19:18.390 So there are a number of different sequence types 00:19:18.390 --> 00:19:21.110 that you can use in order to represent data. 00:19:21.110 --> 00:19:24.580 Another one just so happens to be called a tuple. 00:19:24.580 --> 00:19:27.370 And a tuple is often used if you have a couple of values 00:19:27.370 --> 00:19:30.130 that aren't going to change but you need to store 00:19:30.130 --> 00:19:32.230 a pair of values like two values together, 00:19:32.230 --> 00:19:34.487 or three values together, or something like it. 00:19:34.487 --> 00:19:36.820 You might imagine that if were writing a program to deal 00:19:36.820 --> 00:19:39.770 with graphing in two dimensions, for example, 00:19:39.770 --> 00:19:45.030 you might want to represent a point as an x value and a y value. 00:19:45.030 --> 00:19:46.840 And you could create two variables for it. 00:19:46.840 --> 00:19:49.030 I could say, you know, let me do, say, a coordinate 00:19:49.030 --> 00:19:51.760 x is going to be equal to 10.0. 00:19:51.760 --> 00:19:54.560 And coordinate y is equal to 20.0. 00:19:54.560 --> 00:19:59.650 But now I'm creating two variables for what's really one unit that 00:19:59.650 --> 00:20:02.140 just so happens to have two parts. 00:20:02.140 --> 00:20:05.410 And so to represent this, we can use a tuple in Python, 00:20:05.410 --> 00:20:12.280 and just say something like coordinate equals 10.0 comma 20.0. 00:20:12.280 --> 00:20:15.190 So whereas in lists we use square brackets to denote 00:20:15.190 --> 00:20:18.280 where the list begins and where the list ends, in a tuple, 00:20:18.280 --> 00:20:21.760 we just use parentheses to say we're grouping a number of values together, 00:20:21.760 --> 00:20:25.610 we're grouping one value, 10.0, with a second value, 20.0. 00:20:25.610 --> 00:20:28.780 And now we can pass around these two values as a single unit 00:20:28.780 --> 00:20:32.590 just by referencing them using the single name, which in this case 00:20:32.590 --> 00:20:34.090 is coordinate. 00:20:34.090 --> 00:20:37.500 So there are a number of different types of these various different sequences. 00:20:37.500 --> 00:20:41.980 And some of those sequences we'll take a look at are these data structures here. 00:20:41.980 --> 00:20:45.120 So list, for example, is a sequence of mutable values, 00:20:45.120 --> 00:20:46.120 which we took a look at. 00:20:46.120 --> 00:20:49.360 And mutable, just meaning we can change the elements in the list. 00:20:49.360 --> 00:20:52.310 If I have a list, I can add something to the end of the list, 00:20:52.310 --> 00:20:56.370 I can delete something from the list, I can modify the values inside the list. 00:20:56.370 --> 00:20:59.860 A tuple, on the other hand, is a sequence of immutable values 00:20:59.860 --> 00:21:03.910 those values can't change you can't add another element to the existing tuple. 00:21:03.910 --> 00:21:06.478 You'd have to create a new tuple in order to do so. 00:21:06.478 --> 00:21:08.770 And there are other data structures that exist as well. 00:21:08.770 --> 00:21:10.687 A couple that we'll take a look at in a moment 00:21:10.687 --> 00:21:14.387 include sets, which are a collection of unique values. 00:21:14.387 --> 00:21:16.970 So if you're familiar with sets from the world of mathematics, 00:21:16.970 --> 00:21:20.260 it's a very similar idea, that whereas a list in a tuple 00:21:20.260 --> 00:21:23.200 keeps things in a particular order, a set 00:21:23.200 --> 00:21:25.240 does not keep things in any particular order. 00:21:25.240 --> 00:21:26.710 It's just a collection. 00:21:26.710 --> 00:21:29.560 And in particular, all of the values need to be unique. 00:21:29.560 --> 00:21:31.990 In a list or in a tuple, you might have the same value 00:21:31.990 --> 00:21:33.070 appearing multiple times. 00:21:33.070 --> 00:21:36.650 And in a set, every value appears exactly once. 00:21:36.650 --> 00:21:38.650 And there are some advantages to sets, some ways 00:21:38.650 --> 00:21:40.660 that you can make your programs more efficient 00:21:40.660 --> 00:21:43.618 by using sets if you know that you just need a collection, 00:21:43.618 --> 00:21:45.910 if you don't care about the order, if something is only 00:21:45.910 --> 00:21:48.320 going to show up exactly once at most, then 00:21:48.320 --> 00:21:51.590 you can use a set to potentially make your programs a little more efficient 00:21:51.590 --> 00:21:53.900 and a little more elegantly designed. 00:21:53.900 --> 00:21:56.600 And finally, one other data structure that's quite powerful 00:21:56.600 --> 00:21:59.570 and that's going to come up a number of times during this course 00:21:59.570 --> 00:22:00.740 is a dictionary. 00:22:00.740 --> 00:22:02.750 In Python, shortened to just a dict, which 00:22:02.750 --> 00:22:06.270 is the collection of what we're going to call key-value pairs. 00:22:06.270 --> 00:22:09.470 And the way I like to think of this as with an actual physical dictionary 00:22:09.470 --> 00:22:12.830 that you might find in the library that maps words to their definitions. 00:22:12.830 --> 00:22:16.850 In a physical dictionary, you open up the dictionary and you look up a word 00:22:16.850 --> 00:22:18.890 and you get the definition. 00:22:18.890 --> 00:22:21.000 And a dict in Python is going to be very similar. 00:22:21.000 --> 00:22:23.690 It's going to be a data structure where I can look something up 00:22:23.690 --> 00:22:28.490 by one keyword or one value and get some other value as a result. 00:22:28.490 --> 00:22:31.550 We call the thing that I'm looking up the key. 00:22:31.550 --> 00:22:35.060 And we call what I get when I do the looking up the value. 00:22:35.060 --> 00:22:37.070 So we keep pairs of keys and values. 00:22:37.070 --> 00:22:40.010 In the case of an actual dictionary in the real world, 00:22:40.010 --> 00:22:44.020 the key is the word that we want to look up and the value is its definition. 00:22:44.020 --> 00:22:48.320 But we can use this more generally in Python anytime we want to map something 00:22:48.320 --> 00:22:52.280 to some other value such that we can very easily look up that value 00:22:52.280 --> 00:22:54.290 inside of this data structure. 00:22:54.290 --> 00:22:57.450 So we'll see examples of dictionaries as well. 00:22:57.450 --> 00:23:00.560 So let's now explore the first of these data structures, these lists, 00:23:00.560 --> 00:23:03.230 to explore what we can do by taking advantage 00:23:03.230 --> 00:23:07.500 of the features that are given to us by a Python list, for example. 00:23:07.500 --> 00:23:14.078 So we'll go ahead and create a new program that I'll call lists.py. 00:23:14.078 --> 00:23:17.280 And here, I'm just going to create a list of names. 00:23:17.280 --> 00:23:23.530 So names equals Harry, Ron, Hermione, and Ginny, for example. 00:23:23.530 --> 00:23:25.630 And as I start to write multiple lines of code, 00:23:25.630 --> 00:23:28.240 especially as my Python programs start getting longer, 00:23:28.240 --> 00:23:31.110 it can be a good idea to document what it is that I'm doing. 00:23:31.110 --> 00:23:34.740 So I can say, let me add a comment to this particular line of code 00:23:34.740 --> 00:23:37.770 just so I know what it is that I've done in this line of code. 00:23:37.770 --> 00:23:40.770 And in Python, there are a couple of different ways to create a comment. 00:23:40.770 --> 00:23:45.570 But the simplest way is just to use the pound sign or the hashtag. 00:23:45.570 --> 00:23:48.420 As soon as you include that, everything after that for the remainder 00:23:48.420 --> 00:23:50.290 of the line is a comment. 00:23:50.290 --> 00:23:52.290 The interpreter is going to ignore that comment. 00:23:52.290 --> 00:23:53.790 You can say whatever you want. 00:23:53.790 --> 00:23:56.400 It's more for you, the programmer and for someone 00:23:56.400 --> 00:23:59.130 who's reading your program to be able to look at the program, 00:23:59.130 --> 00:24:03.280 understand what it's saying, and figure out what they need to do about it. 00:24:03.280 --> 00:24:06.000 So I can just say, define a list of names, 00:24:06.000 --> 00:24:09.960 for example, just to make it clear to me what it is that I have done inside 00:24:09.960 --> 00:24:11.640 of this line of code. 00:24:11.640 --> 00:24:14.940 So I can print out that list of names, as we've done before. 00:24:14.940 --> 00:24:19.005 And we'll see that when I print out that list of names, what I get is-- 00:24:19.005 --> 00:24:21.930 oh, let me run list.py. 00:24:21.930 --> 00:24:23.100 What I get is this list-- 00:24:23.100 --> 00:24:25.468 Harry, Ron, Hermione, and Ginny. 00:24:25.468 --> 00:24:27.510 But I could also print out, as we've seen before, 00:24:27.510 --> 00:24:28.760 just the first of those names. 00:24:28.760 --> 00:24:32.460 Say, you know, print out just names square bracket zero, in which case 00:24:32.460 --> 00:24:36.190 I'm going to get just Harry, for example. 00:24:36.190 --> 00:24:38.440 But now, recall that a list is mutable. 00:24:38.440 --> 00:24:42.340 I can modify the elements that happen to exist inside of this list. 00:24:42.340 --> 00:24:49.630 So I could say names.apped a new name, something like Draco, for example. 00:24:49.630 --> 00:24:53.340 And so lists have a number of built in methods or functions which 00:24:53.340 --> 00:24:56.550 are functions that I can run on an existing list 00:24:56.550 --> 00:24:58.590 to access particular elements of the list 00:24:58.590 --> 00:25:00.780 or to modify the list in particular ways. 00:25:00.780 --> 00:25:03.810 And in the case of a list, the append method 00:25:03.810 --> 00:25:06.420 is a method or function that I can run that just adds 00:25:06.420 --> 00:25:09.010 a value to the end of an existing list. 00:25:09.010 --> 00:25:11.090 So I've added Draco to the list. 00:25:11.090 --> 00:25:14.610 And there are a number of other methods that I can use on lists, one of which 00:25:14.610 --> 00:25:16.523 is, for example, sorting a list. 00:25:16.523 --> 00:25:18.690 No need to write your own sorting algorithm in order 00:25:18.690 --> 00:25:20.670 to sort a sequence of objects. 00:25:20.670 --> 00:25:23.640 In Python, there is a built-in sort method 00:25:23.640 --> 00:25:26.850 that works on lists where I can just say names.sort. 00:25:26.850 --> 00:25:29.340 That will automatically sort everything in the list. 00:25:29.340 --> 00:25:32.220 And now if I print out all of those names-- 00:25:32.220 --> 00:25:36.960 go to print them out and get rid of this old print statement-- 00:25:36.960 --> 00:25:39.450 now we see that we get five names that are printed out 00:25:39.450 --> 00:25:42.150 because I had four elements originally in this list 00:25:42.150 --> 00:25:43.710 but then I added a fifth one. 00:25:43.710 --> 00:25:46.750 And notice now that they are actually in alphabetical order, 00:25:46.750 --> 00:25:49.140 starting with Draco, ending with Ron because I 00:25:49.140 --> 00:25:52.920 was able to sort the list by modifying the order in which those elements 00:25:52.920 --> 00:25:54.820 actually show up. 00:25:54.820 --> 00:25:56.940 And so list can definitely quite powerful anytime 00:25:56.940 --> 00:25:59.520 you need to store elements in order, a list 00:25:59.520 --> 00:26:02.940 is definitely a useful tool that Python gives to you. 00:26:02.940 --> 00:26:05.430 If you don't care about the order of the elements though, 00:26:05.430 --> 00:26:08.550 and if you know that all the elements are going to be unique, 00:26:08.550 --> 00:26:11.130 then you can use a set, which is another Python data 00:26:11.130 --> 00:26:12.960 structure that works in similar ways. 00:26:12.960 --> 00:26:15.820 The syntax is slightly different. 00:26:15.820 --> 00:26:17.550 So let's do an example with those. 00:26:17.550 --> 00:26:20.940 I'll create a new file, call it sets.py. 00:26:20.940 --> 00:26:23.490 And let me first create an empty set. 00:26:23.490 --> 00:26:25.540 And we can do that by just saying s equals-- 00:26:25.540 --> 00:26:28.410 s is going to be the variable that will store my set. 00:26:28.410 --> 00:26:30.990 And I'll say set and then parentheses. 00:26:30.990 --> 00:26:33.570 That will just create an empty set that just so happens 00:26:33.570 --> 00:26:38.190 to have nothing inside of it now we'll add some elements to the set 00:26:38.190 --> 00:26:40.800 so I can say s.add. 00:26:40.800 --> 00:26:42.900 Let's add the number one to the set. 00:26:42.900 --> 00:26:44.700 Let's add the number two. 00:26:44.700 --> 00:26:45.900 Let's add that number three. 00:26:45.900 --> 00:26:48.430 And let's add the number four. 00:26:48.430 --> 00:26:52.550 And then we can print out the set to see what happens to be inside the set 00:26:52.550 --> 00:26:54.350 right now. 00:26:54.350 --> 00:27:00.520 Now when I run this program, Python sets.py, we see that inside the set 00:27:00.520 --> 00:27:03.205 are four values, one, two, three, and four. 00:27:03.205 --> 00:27:04.330 They happen to be in order. 00:27:04.330 --> 00:27:05.980 But sets are not naturally ordered. 00:27:05.980 --> 00:27:08.980 They're not going to always keep track of what the order is going to be. 00:27:08.980 --> 00:27:09.950 But I can add-- 00:27:09.950 --> 00:27:14.580 for example, if I add three again to the set, now I've added three to the set 00:27:14.580 --> 00:27:15.130 twice. 00:27:15.130 --> 00:27:19.060 I added one, two, three, four, and then three again. 00:27:19.060 --> 00:27:20.770 When I print out the contents of the set, 00:27:20.770 --> 00:27:24.400 it still just contains the elements one, two, three, four. 00:27:24.400 --> 00:27:26.790 No element ever appears twice in the set, 00:27:26.790 --> 00:27:28.540 following with the mathematical definition 00:27:28.540 --> 00:27:34.160 of a set where no element ever appears more than once inside of a set. 00:27:34.160 --> 00:27:36.360 You can also remove elements from sets as well. 00:27:36.360 --> 00:27:39.720 So if I wanted to remove the number two from the set for example, 00:27:39.720 --> 00:27:43.760 I could say s.remove2 and then print out s 00:27:43.760 --> 00:27:48.060 to say print out whatever happens to be inside of that set now. 00:27:48.060 --> 00:27:50.170 And now when I rerun this program, I only 00:27:50.170 --> 00:27:55.090 get one, three, and four because I removed two from the set. 00:27:55.090 --> 00:27:57.670 So sets allow you to add to them, remove from them. 00:27:57.670 --> 00:28:02.020 And also, all sequences, whether they be strings, or lists, or sets, 00:28:02.020 --> 00:28:04.550 allow you to get how many elements are in the set 00:28:04.550 --> 00:28:08.830 by taking advantage of a function built into Python called len. 00:28:08.830 --> 00:28:12.550 So len we'll give you the length of a sequence, so the number of items 00:28:12.550 --> 00:28:16.000 inside of a list, or the number of characters inside of a string, 00:28:16.000 --> 00:28:18.700 or the number of elements inside of a set. 00:28:18.700 --> 00:28:22.030 And so, if I wanted to print out how many elements are in the set, 00:28:22.030 --> 00:28:23.650 I might do something like this. 00:28:23.650 --> 00:28:31.390 In a formatted string, say the set has some number of elements. 00:28:31.390 --> 00:28:33.110 And how do I know how many elements? 00:28:33.110 --> 00:28:35.210 Well, again, inside of these curly braces, 00:28:35.210 --> 00:28:38.510 I can include any expression in Python that I would 00:28:38.510 --> 00:28:40.980 like to substitute into this string. 00:28:40.980 --> 00:28:42.590 So how many elements are in the set? 00:28:42.590 --> 00:28:47.210 I can get that by calculating len of s. 00:28:47.210 --> 00:28:50.165 So what I've done here is I've said with len of s, 00:28:50.165 --> 00:28:53.070 I would like to calculate the length of the set, s, in other words, 00:28:53.070 --> 00:28:55.940 how many elements are actually inside of that set. 00:28:55.940 --> 00:28:59.150 And then using this curly brace notation, I'm saying, take that number 00:28:59.150 --> 00:29:01.460 and plug it into this string so we can see the set 00:29:01.460 --> 00:29:05.610 has some number of elements, for example. 00:29:05.610 --> 00:29:09.338 So now if I run this program, Python sets.py, 00:29:09.338 --> 00:29:12.380 I see that I get these three elements that happen to be inside of the set 00:29:12.380 --> 00:29:15.590 right now, which is one, and then three, and then four. 00:29:15.590 --> 00:29:19.160 And then it tells me that the set has three elements inside 00:29:19.160 --> 00:29:23.515 of it, which is the number of elements that are in the set right now. 00:29:23.515 --> 00:29:26.640 So now we've seen a number of different language features inside of Python. 00:29:26.640 --> 00:29:28.035 We've seen variables. 00:29:28.035 --> 00:29:31.160 We've seen conditions so that we can conditionally do things-- if something 00:29:31.160 --> 00:29:33.260 is true, if something else is true. 00:29:33.260 --> 00:29:35.300 And we've seen some of the data structures 00:29:35.300 --> 00:29:38.990 that are core to the way Python works-- lists, and sets, and tuples, 00:29:38.990 --> 00:29:41.293 and other data structures that can be helpful too. 00:29:41.293 --> 00:29:44.210 And now let's take a look at another feature of the Python programming 00:29:44.210 --> 00:29:47.390 language common to many programming languages, the idea of looping. 00:29:47.390 --> 00:29:50.730 If I want to be able to do something multiple times, 00:29:50.730 --> 00:29:54.182 I'll go ahead and create a new file called loops.py. 00:29:54.182 --> 00:29:56.180 And let's just create a simple loop. 00:29:56.180 --> 00:29:58.280 The simplest loop we could create in Python 00:29:58.280 --> 00:30:01.350 is just one that's going to count a bunch of numbers. 00:30:01.350 --> 00:30:04.520 So in order to do that, what I could say is something like this. 00:30:04.520 --> 00:30:10.070 For i in one, two, three, four, five-- 00:30:10.070 --> 00:30:12.880 or maybe I want to count zero, one, two, three, four, five, just 00:30:12.880 --> 00:30:14.500 to start counting at zero-- 00:30:14.500 --> 00:30:17.111 print i. 00:30:17.111 --> 00:30:20.740 And so here's the basic syntax for a Python loop. 00:30:20.740 --> 00:30:22.510 And here's what seems to be going on. 00:30:22.510 --> 00:30:25.600 Over here on the very first line, I have a Python list 00:30:25.600 --> 00:30:29.540 as denoted by those square brackets that contains six numbers-- 00:30:29.540 --> 00:30:31.750 zero, one, two, three, four, five. 00:30:31.750 --> 00:30:35.930 And now I have a for loop-- for i in this list. 00:30:35.930 --> 00:30:39.640 And the way Python interprets this is to say, go through this list one 00:30:39.640 --> 00:30:40.860 element to the time. 00:30:40.860 --> 00:30:43.557 And for each element, call that element i. 00:30:43.557 --> 00:30:44.890 You could've called it anything. 00:30:44.890 --> 00:30:47.140 But in this case, i is just a conventional choice 00:30:47.140 --> 00:30:49.000 for a number that keeps incrementing. 00:30:49.000 --> 00:30:52.810 And we're going to print out now the value of i 00:30:52.810 --> 00:30:55.670 for each iteration of this loop. 00:30:55.670 --> 00:31:00.400 So we try this out now and run Python loops.py. 00:31:00.400 --> 00:31:02.920 We see zero, one, two, three, four, five. 00:31:02.920 --> 00:31:06.880 Great, it printed out all of the numbers from zero to five one at a time. 00:31:06.880 --> 00:31:10.300 In practice though, if we wanted to count all the way up to five 00:31:10.300 --> 00:31:14.470 or print six numbers for example, this is fine for now. 00:31:14.470 --> 00:31:17.470 But if we wanted to print like 100 numbers or 1,000 numbers, 00:31:17.470 --> 00:31:19.720 this is going to start to get tedious. 00:31:19.720 --> 00:31:23.920 So Python has a built-in function called range 00:31:23.920 --> 00:31:28.480 where I can say for i in range six to achieve exactly the same thing. 00:31:28.480 --> 00:31:31.520 Range six means get me a range of six numbers. 00:31:31.520 --> 00:31:35.140 So if we start at zero, it's going to go from zero all the way up to five. 00:31:35.140 --> 00:31:39.130 And then we can print out each one of the elements inside of that sequence. 00:31:39.130 --> 00:31:44.770 So if I rerun in Python loops.py, we get zero, one, two, three, four, five. 00:31:44.770 --> 00:31:48.250 So loops enable us to loop over any type of sequence. 00:31:48.250 --> 00:31:51.220 So if the sequence is a list, I can say something like, 00:31:51.220 --> 00:31:56.830 if I have a list of names like Harry, and Ron, and Hermione, 00:31:56.830 --> 00:31:59.370 and this is my list of names, I can have a loop that 00:31:59.370 --> 00:32:05.010 says that for each name in my list of names, let's print out that name, 00:32:05.010 --> 00:32:06.200 for example. 00:32:06.200 --> 00:32:07.020 So we have a list. 00:32:07.020 --> 00:32:08.180 The list is called Names. 00:32:08.180 --> 00:32:11.520 We're looping over it one element at a time and printing it out. 00:32:11.520 --> 00:32:15.510 Now if I run the program, I see three names printed one on each line. 00:32:15.510 --> 00:32:17.880 And you can do this for other sequences as well. 00:32:17.880 --> 00:32:21.660 Maybe I have just a single name that is called Harry. 00:32:21.660 --> 00:32:26.190 And now I can have a line that says, you know, for every character in that name, 00:32:26.190 --> 00:32:28.500 print the character. 00:32:28.500 --> 00:32:31.740 If the name is the sequence, is a sequence of individual characters 00:32:31.740 --> 00:32:34.620 because it's a string, then when I loop over to that string, 00:32:34.620 --> 00:32:38.140 I'll be looping over each individual character in that string. 00:32:38.140 --> 00:32:41.190 So I can run the program and see one on each line, 00:32:41.190 --> 00:32:47.280 each of the letters that just so happens to be inside of Harry's name. 00:32:47.280 --> 00:32:48.570 So now we've seen conditions. 00:32:48.570 --> 00:32:49.390 We've seen loops. 00:32:49.390 --> 00:32:51.598 And we've seen a number of different data structures. 00:32:51.598 --> 00:32:54.678 We've seen lists, and sets, as well as tuples. 00:32:54.678 --> 00:32:57.720 The last important data structure that we're going to be taking a look at 00:32:57.720 --> 00:33:00.120 are Python dictionaries which, as you'll recall, 00:33:00.120 --> 00:33:02.490 are some mapping of keys to values. 00:33:02.490 --> 00:33:04.290 If I want to be able to look something up, 00:33:04.290 --> 00:33:07.260 I can use a Python dictionary just as a data structure to be 00:33:07.260 --> 00:33:09.930 able to store these sorts of values. 00:33:09.930 --> 00:33:13.797 So I'll create a new file called dictionaries.py. 00:33:13.797 --> 00:33:15.630 And maybe I want to create a dictionary that 00:33:15.630 --> 00:33:20.760 is going to keep track of, say, what house each of the students at Hogwarts 00:33:20.760 --> 00:33:21.910 happen to be in. 00:33:21.910 --> 00:33:24.630 So I might have a dictionary called Houses. 00:33:24.630 --> 00:33:28.080 And the way we specify a dictionary is by specifying 00:33:28.080 --> 00:33:32.880 a key colon of value when we're defining a dictionary for the first time. 00:33:32.880 --> 00:33:41.360 So I might say Harry colon Gryffindor and then Draco colon Slytherin, 00:33:41.360 --> 00:33:42.870 for example. 00:33:42.870 --> 00:33:46.280 And so what this line of code is doing is it is creating a new dictionary. 00:33:46.280 --> 00:33:47.990 This dictionary is called Houses. 00:33:47.990 --> 00:33:52.250 And inside this dictionary, I have two keys, two things that I can look up. 00:33:52.250 --> 00:33:55.070 I can look up Harry or I can look up Draco. 00:33:55.070 --> 00:34:00.170 And when I look up those keys, I get the value that follows their colon. 00:34:00.170 --> 00:34:04.400 So after Harry, if I look up Harry, I get Gryffindor If I look up Draco, 00:34:04.400 --> 00:34:05.900 I get Slytherin, for example. 00:34:08.750 --> 00:34:13.090 So now if I wanted to print out what house Harry is in, 00:34:13.090 --> 00:34:18.060 I can print out houses square brackets Harry. 00:34:18.060 --> 00:34:20.409 So I can here, say, I would like to print out-- 00:34:20.409 --> 00:34:22.300 take the Houses dictionary. 00:34:22.300 --> 00:34:24.760 And the square bracket notation is how I look 00:34:24.760 --> 00:34:26.800 something up inside of a dictionary. 00:34:26.800 --> 00:34:30.040 It's similar to how we use square brackets to look up a specific element 00:34:30.040 --> 00:34:34.000 inside of a list to say, like, get me element zero or element one. 00:34:34.000 --> 00:34:36.400 In this case, we're using a Python dictionary 00:34:36.400 --> 00:34:40.360 to say, take the Houses dictionary and look up Harry's value, which 00:34:40.360 --> 00:34:42.070 hopefully should be Gryffindor. 00:34:42.070 --> 00:34:45.340 And we'll see that if we look up run Python dictionaries.py, 00:34:45.340 --> 00:34:49.810 we do get Gryffindor as the value of Harry's house. 00:34:49.810 --> 00:34:53.650 We can add things to this dictionary as well using the same syntax. 00:34:53.650 --> 00:34:56.620 In the same way that I use square brackets to access the value 00:34:56.620 --> 00:35:00.190 inside of a dictionary, if I want to change the value in a dictionary 00:35:00.190 --> 00:35:06.040 or add something new to it, I could say houses and Hermione, 00:35:06.040 --> 00:35:10.580 and say that Hermione is also in Gryffindor, for example. 00:35:10.580 --> 00:35:14.740 And so this line of code here says take the Houses dictionary 00:35:14.740 --> 00:35:18.130 and look up Hermione in the Houses dictionary. 00:35:18.130 --> 00:35:22.380 And when you do, that should be set equal to this value here, Gryffindor. 00:35:22.380 --> 00:35:26.470 So we took that value and we are going to assign it to Hermione inside 00:35:26.470 --> 00:35:29.090 of the dictionary, such that now if we wanted to, 00:35:29.090 --> 00:35:33.400 we could print out Hermione's house as well, run the program, 00:35:33.400 --> 00:35:36.040 and see that Hermione is also in Gryffindor. 00:35:36.040 --> 00:35:39.470 So anytime we want to be able to map some value to some other volume, 00:35:39.470 --> 00:35:42.160 whether we're mapping people to what house they happen to be in 00:35:42.160 --> 00:35:44.890 or we're mapping users to some information about those users 00:35:44.890 --> 00:35:46.960 inside of our web application, dictionaries 00:35:46.960 --> 00:35:52.570 are going to be very, very powerful tools for us to be able to do that. 00:35:52.570 --> 00:35:54.900 The next Python language feature we'll take a look at 00:35:54.900 --> 00:35:56.760 are functions in Python that are going to be 00:35:56.760 --> 00:35:59.880 some way for us to write our own functions that take an input 00:35:59.880 --> 00:36:01.107 and produce some output. 00:36:01.107 --> 00:36:04.440 We've already seen a number of different functions that already exist in Python. 00:36:04.440 --> 00:36:07.770 We've seen the input function that takes an input from the user. 00:36:07.770 --> 00:36:11.280 We've seen the print function that takes some text or some other information 00:36:11.280 --> 00:36:12.760 and prints it to the screen. 00:36:12.760 --> 00:36:17.020 But if we want to define our own functions, we can do so as well. 00:36:17.020 --> 00:36:21.520 So here I'll go ahead and write a new program called functions.py. 00:36:21.520 --> 00:36:25.410 And let's write a function that takes a number and squares it. 00:36:25.410 --> 00:36:28.170 So the square of 10 is 10 times 10, or 100. 00:36:28.170 --> 00:36:33.060 I would like a function that very easily takes a number and returns its square. 00:36:33.060 --> 00:36:36.330 The way I define a function in Python is using the def keyword. 00:36:36.330 --> 00:36:38.100 Def, short for define. 00:36:38.100 --> 00:36:41.640 And here I can say, let me define a function called square 00:36:41.640 --> 00:36:44.340 and then, in parentheses, what inputs it takes. 00:36:44.340 --> 00:36:47.340 In this case, square just takes a single input that I'm going to call x. 00:36:47.340 --> 00:36:50.215 But if there were multiple inputs, I could separate them with commas, 00:36:50.215 --> 00:36:53.680 like x, y, z for a function that took three inputs, for example. 00:36:53.680 --> 00:36:57.170 But in this case, there is just a single input, x. 00:36:57.170 --> 00:36:59.640 And the square function could have any logic 00:36:59.640 --> 00:37:02.430 in it indented underneath the square function. 00:37:02.430 --> 00:37:04.690 But ultimately, this function is fairly simple. 00:37:04.690 --> 00:37:10.490 All it's going to do is return x times x, x multiplied by itself. 00:37:10.490 --> 00:37:14.420 And now, if I want to print out a whole bunch of squares of numbers, 00:37:14.420 --> 00:37:15.410 I can do so. 00:37:15.410 --> 00:37:25.680 I can say for i in range, let's say, 10, let's print out that the square of i 00:37:25.680 --> 00:37:30.110 is square i. 00:37:30.110 --> 00:37:32.110 So let's try and parse out what's going on here. 00:37:32.110 --> 00:37:35.230 Line four says for i in range 10, do some loop 00:37:35.230 --> 00:37:37.957 10 times, looping from zero all the way up to nine. 00:37:37.957 --> 00:37:40.540 And for each time we loop, we're going to print something out. 00:37:40.540 --> 00:37:42.640 We're going to print out the square of-- 00:37:42.640 --> 00:37:44.470 plug in the value of i here-- 00:37:44.470 --> 00:37:51.120 is-- plug in the value of calling our square function using i as input. 00:37:51.120 --> 00:37:54.060 So that is going to have the result of running this loop 10 times 00:37:54.060 --> 00:37:56.490 and printing out this line 10 different times, 00:37:56.490 --> 00:37:59.530 each with a different value of i. 00:37:59.530 --> 00:38:01.263 So I can run Python functions.py. 00:38:01.263 --> 00:38:02.180 And here's what I see. 00:38:02.180 --> 00:38:03.380 The square of zero is zero. 00:38:03.380 --> 00:38:06.270 Square of one is one two is four, so on and so forth, 00:38:06.270 --> 00:38:09.170 all the way up to the square of 9 is 81. 00:38:09.170 --> 00:38:12.050 So we've now written a function and been able to use it. 00:38:12.050 --> 00:38:13.820 But ideally, when we write functions, we'd 00:38:13.820 --> 00:38:16.830 like to not just be able to use them in the same file, 00:38:16.830 --> 00:38:20.060 but for others to be able to use them as well. 00:38:20.060 --> 00:38:21.600 And so how can we do that? 00:38:21.600 --> 00:38:25.790 Well, in order to do that, you can import functions from other Python 00:38:25.790 --> 00:38:28.290 modules or files, so to speak. 00:38:28.290 --> 00:38:33.740 So let me create a new file called squares.py, for example. 00:38:33.740 --> 00:38:37.100 So that instead of running this loop here, let's 00:38:37.100 --> 00:38:41.330 instead run this loop in squares.py, again, 00:38:41.330 --> 00:38:43.220 separating out different parts of my code. 00:38:43.220 --> 00:38:47.690 I have one file that defines the square function inside of functions.py 00:38:47.690 --> 00:38:50.630 and then another file called squares.py, where I'm actually 00:38:50.630 --> 00:38:54.440 calling the square function. 00:38:54.440 --> 00:38:58.818 Now if I try to run Python squares.py, you'll notice I'll run into an error. 00:38:58.818 --> 00:39:00.860 Here's another error you'll see quite frequently. 00:39:00.860 --> 00:39:03.140 It's a name error, another type of exception. 00:39:03.140 --> 00:39:06.765 Which here says the name square is not defined, 00:39:06.765 --> 00:39:09.140 meaning I'm trying to use a variable, or a function name, 00:39:09.140 --> 00:39:12.380 or something else that doesn't actually have a definition. 00:39:12.380 --> 00:39:14.900 I've never said what square is. 00:39:14.900 --> 00:39:18.470 And that's because by default, Python files don't know about each other. 00:39:18.470 --> 00:39:22.250 If I want to use a function that was defined inside of another file, 00:39:22.250 --> 00:39:24.860 I need to import it from that file. 00:39:24.860 --> 00:39:26.900 And I can do so like so. 00:39:26.900 --> 00:39:32.370 I can say, from functions import square. 00:39:32.370 --> 00:39:35.310 Functions was the name of this file, functions.py. 00:39:35.310 --> 00:39:38.040 And I'm saying from that Python module, I 00:39:38.040 --> 00:39:42.980 would like to import the square function as a function that I would like to use. 00:39:42.980 --> 00:39:47.040 Now I can run Python squares.py. 00:39:47.040 --> 00:39:48.660 And we get the output that we expect-- 00:39:48.660 --> 00:39:50.370 no more exception. 00:39:50.370 --> 00:39:55.050 I've now been able to import something from another module 00:39:55.050 --> 00:39:56.630 and access it this way. 00:39:56.630 --> 00:39:58.380 So this is one way to import, to literally 00:39:58.380 --> 00:40:01.110 say from functions import a square function, 00:40:01.110 --> 00:40:05.410 import a particular name that is defined inside of functions.py. 00:40:05.410 --> 00:40:09.600 Another way I could have done this is just to say import functions, just 00:40:09.600 --> 00:40:11.400 import that whole module. 00:40:11.400 --> 00:40:13.770 But then I would need to say-- instead of just square, 00:40:13.770 --> 00:40:19.290 I would need to say functions.square, to mean go inside the functions module, 00:40:19.290 --> 00:40:22.770 and get the square function, and run that function. 00:40:22.770 --> 00:40:25.512 And this would operate in exactly the same way. 00:40:25.512 --> 00:40:26.970 So, a couple of different options-- 00:40:26.970 --> 00:40:29.790 I either import the entire module, in which case 00:40:29.790 --> 00:40:34.260 I use this dot notation to, say, access a particular part of that module. 00:40:34.260 --> 00:40:37.680 Or I say from functions import square to just 00:40:37.680 --> 00:40:42.180 import the name square into this file entirely so that I can just 00:40:42.180 --> 00:40:45.210 use the word square whenever I want to. 00:40:45.210 --> 00:40:48.300 And this works not just for modules that we have written, 00:40:48.300 --> 00:40:51.150 but also Python comes with the number of built-in modules. 00:40:51.150 --> 00:40:54.000 If you want to write programs that interact with CSV files, which 00:40:54.000 --> 00:40:58.350 are a spreadsheet file format, I can import Python's built-in CSV module 00:40:58.350 --> 00:41:00.970 to get access to a whole bunch of CSV-related features. 00:41:00.970 --> 00:41:02.970 There are a whole bunch of math-related features 00:41:02.970 --> 00:41:06.160 you can get by importing the math module, so on and so forth. 00:41:06.160 --> 00:41:08.610 And there are additional Python modules and packages 00:41:08.610 --> 00:41:11.140 that you can install that other people have written. 00:41:11.140 --> 00:41:13.290 And then you just import them when the time comes. 00:41:13.290 --> 00:41:16.598 And next time, as we take a look at Django, this is one of the techniques 00:41:16.598 --> 00:41:18.390 that we're going to be looking at, is using 00:41:18.390 --> 00:41:23.550 functions that have been written by people that are not ourselves. 00:41:23.550 --> 00:41:26.100 So that now is modules and how we can use 00:41:26.100 --> 00:41:30.210 modules to be able to import functions in order to allow for certain behavior. 00:41:30.210 --> 00:41:33.870 And this is one way that we can program using the Python programming language. 00:41:33.870 --> 00:41:35.798 But another key technique that Python supports 00:41:35.798 --> 00:41:38.340 that are supported by a number of other programming languages 00:41:38.340 --> 00:41:43.050 as well is an idea of object-oriented programming, a special type 00:41:43.050 --> 00:41:46.030 of programming or programming paradigm, so to speak, 00:41:46.030 --> 00:41:48.900 which is a way of thinking about the way that we write programs. 00:41:48.900 --> 00:41:50.940 In an object-oriented programming, we think 00:41:50.940 --> 00:41:53.730 about the world in terms of objects where 00:41:53.730 --> 00:41:57.300 objects might store information, store some data inside of them, 00:41:57.300 --> 00:42:01.050 and also support the ability to perform types of operations, 00:42:01.050 --> 00:42:04.830 some sort of actions, or methods, or functions, as we might call them, 00:42:04.830 --> 00:42:07.720 that can operate on those objects. 00:42:07.720 --> 00:42:11.700 So now we're going to take a look at some of the object-oriented capacities 00:42:11.700 --> 00:42:15.150 that the Python programming language is going to give us the ability to have. 00:42:15.150 --> 00:42:17.640 So, Python comes with a number of built in types. 00:42:17.640 --> 00:42:19.290 It has types for lists. 00:42:19.290 --> 00:42:21.880 It has types for sets and so on and so forth. 00:42:21.880 --> 00:42:25.380 Let's imagine, though, that we want to create a new type in Python, 00:42:25.380 --> 00:42:29.170 some way of representing other types of data. 00:42:29.170 --> 00:42:31.860 For example, two-dimensional points, things 00:42:31.860 --> 00:42:35.120 we've talked about before-- something that has an x value and a y value. 00:42:35.120 --> 00:42:36.870 Now, as we've already discussed, you could 00:42:36.870 --> 00:42:40.710 do this using a tuple, just using one number comma another number. 00:42:40.710 --> 00:42:44.280 But we could create an entire class of objects 00:42:44.280 --> 00:42:47.127 to be able to represent this data structure as well. 00:42:47.127 --> 00:42:48.960 And so that's what we'll take a look at now, 00:42:48.960 --> 00:42:52.200 is how to create a class in Python. 00:42:52.200 --> 00:42:55.860 So I'll create a new file called classes.py. 00:42:55.860 --> 00:42:58.920 And all a class is, is you can think of a class 00:42:58.920 --> 00:43:01.530 as a template for a type of object. 00:43:01.530 --> 00:43:05.460 We are going to define a new class called Point. 00:43:05.460 --> 00:43:08.340 And then after we've defined what a point is, 00:43:08.340 --> 00:43:10.650 we will be able to create other points. 00:43:10.650 --> 00:43:15.760 We'll be able to create points to store x and y values, for example. 00:43:15.760 --> 00:43:18.060 And so what do we need in order to create a class? 00:43:18.060 --> 00:43:22.820 Well, we need some way to say that when I create a point, what should happen? 00:43:22.820 --> 00:43:24.930 And in Python, this is defined using what's 00:43:24.930 --> 00:43:29.700 called a magic method called underscore underscore init. 00:43:29.700 --> 00:43:33.510 And underscore underscore init is a method or function that 00:43:33.510 --> 00:43:36.210 is going to automatically be called every time that I 00:43:36.210 --> 00:43:38.940 try to create a new point. 00:43:38.940 --> 00:43:42.000 And this function takes a couple of arguments. 00:43:42.000 --> 00:43:46.530 All functions that operate on objects themselves, otherwise known as methods, 00:43:46.530 --> 00:43:49.920 are going to take the first argument called self. 00:43:49.920 --> 00:43:54.330 And this argument self represents the object in question. 00:43:54.330 --> 00:43:56.700 And this is going to be important because we don't just 00:43:56.700 --> 00:44:00.210 want a single variable called x to store the points x coordinate 00:44:00.210 --> 00:44:03.240 or a single variable called y to store the y coordinate 00:44:03.240 --> 00:44:07.528 because two different points might have different x and different y values. 00:44:07.528 --> 00:44:09.570 And we want to be able to store those separately. 00:44:09.570 --> 00:44:13.360 And we're going to store them inside of the object itself. 00:44:13.360 --> 00:44:16.842 So this variable self references the object 00:44:16.842 --> 00:44:18.300 that we are currently dealing with. 00:44:18.300 --> 00:44:20.490 And it might change depending on which point we 00:44:20.490 --> 00:44:23.790 happen to be interacting with at any given time. 00:44:23.790 --> 00:44:25.530 What other inputs does a point need? 00:44:25.530 --> 00:44:29.140 Well, a point also needs an x value and a y value. 00:44:29.140 --> 00:44:32.160 So when we create a point, we're going to provide to that point 00:44:32.160 --> 00:44:35.410 an x value and a y value. 00:44:35.410 --> 00:44:37.680 Now, what do we need to do in order to store 00:44:37.680 --> 00:44:40.170 all this data inside of the point? 00:44:40.170 --> 00:44:44.290 Well, recall that self is representing the point itself. 00:44:44.290 --> 00:44:46.850 And so if we want to store data inside of that point 00:44:46.850 --> 00:44:50.340 and allow that point to store its own x and y values, 00:44:50.340 --> 00:44:55.950 then we need to store that data inside of the self, so to speak. 00:44:55.950 --> 00:45:01.110 And in order to do that, we can use this dot notation to say self.x 00:45:01.110 --> 00:45:04.980 is equal to whatever this input x happens to be. 00:45:04.980 --> 00:45:10.950 And self.y is equal to whatever this argument y happens to be. 00:45:10.950 --> 00:45:13.290 And these values, x and y, they can be called anything. 00:45:13.290 --> 00:45:17.390 They could just be called like input one and input two, for example. 00:45:17.390 --> 00:45:19.140 And then you would just reflect them here. 00:45:19.140 --> 00:45:21.930 The important thing is that these two input values 00:45:21.930 --> 00:45:25.320 are being stored inside of the point itself 00:45:25.320 --> 00:45:28.440 in properties that we're going to call x and y. 00:45:30.945 --> 00:45:32.820 All right, so that was a little bit abstract. 00:45:32.820 --> 00:45:35.160 But now let's see how we could actually use this. 00:45:35.160 --> 00:45:41.050 If I want to create a new point called p, I can say p equals point. 00:45:41.050 --> 00:45:44.375 And then the self argument is going to be provided automatically. 00:45:44.375 --> 00:45:45.750 I don't need to worry about that. 00:45:45.750 --> 00:45:48.330 But I do need to provide input one and input two-- 00:45:48.330 --> 00:45:51.220 the x value and the y value. 00:45:51.220 --> 00:45:54.900 So I'll go ahead and provide an x value of two and a y value of eight, 00:45:54.900 --> 00:45:56.230 for example. 00:45:56.230 --> 00:45:58.210 So now I've created this point. 00:45:58.210 --> 00:46:01.380 And now that I have a point, I can print out information about the point. 00:46:01.380 --> 00:46:03.660 I can print out the x value of the point. 00:46:03.660 --> 00:46:06.480 And I can print out the y value of the point. 00:46:06.480 --> 00:46:10.290 Again, I'm using this dot notation to say, go into the point 00:46:10.290 --> 00:46:13.440 and access data that is stored inside of that point. 00:46:13.440 --> 00:46:17.460 Access its x value and access its y value. 00:46:17.460 --> 00:46:22.380 So now when I run this program, Python classes.py, what I get 00:46:22.380 --> 00:46:25.530 is two on the first line, that is the x value, and then 00:46:25.530 --> 00:46:28.110 eight on the second-- or eight on the second line. 00:46:28.110 --> 00:46:30.340 That is the y value. 00:46:30.340 --> 00:46:32.310 So what we have here is a function called 00:46:32.310 --> 00:46:37.560 init that creates a point by storing the two inputs inside of the object, 00:46:37.560 --> 00:46:39.930 inside of a property called x and a property 00:46:39.930 --> 00:46:43.920 called y, such that later I can create a point which 00:46:43.920 --> 00:46:46.350 calls this init function implicitly. 00:46:46.350 --> 00:46:49.590 And after we've created the point, I can access the data inside of it. 00:46:49.590 --> 00:46:52.560 I can say print out whatever p.x is equal to, 00:46:52.560 --> 00:46:57.480 print out whatever p.y is equal to as well. 00:46:57.480 --> 00:47:00.150 So that was a fairly simple example of creating a class, 00:47:00.150 --> 00:47:04.450 just creating a class for representing a point, an x and a y value. 00:47:04.450 --> 00:47:06.190 Let's look at a more interesting example. 00:47:06.190 --> 00:47:09.148 Let's imagine that we're trying to write a program for an airline where 00:47:09.148 --> 00:47:12.292 the airline needs to keep track of booking passengers on a flight 00:47:12.292 --> 00:47:14.250 and making sure that no flight gets overbooked. 00:47:14.250 --> 00:47:16.190 We don't want more passengers on the flight 00:47:16.190 --> 00:47:19.260 than there is capacity on that flight. 00:47:19.260 --> 00:47:22.770 So let's define a new class that we're going to call flight. 00:47:22.770 --> 00:47:25.320 And this time, the init method is just going 00:47:25.320 --> 00:47:29.760 to take a single argument other than the self, which is the capacity. 00:47:29.760 --> 00:47:32.160 Every flight needs some sort of capacity to know 00:47:32.160 --> 00:47:34.690 how many people can fit on the plane. 00:47:34.690 --> 00:47:39.660 And so I'll store that inside of a value called self.capacity equals capacity. 00:47:39.660 --> 00:47:43.220 And what other information do we need to store about a flight? 00:47:43.220 --> 00:47:44.880 Well, a flight has a capacity. 00:47:44.880 --> 00:47:48.280 And it also has all of the passengers on the flight. 00:47:48.280 --> 00:47:50.520 And so we could represent this in a number of ways. 00:47:50.520 --> 00:47:54.850 But we know that lists can be used in order to store a sequence of values. 00:47:54.850 --> 00:47:57.000 So we'll go ahead and just create a list that 00:47:57.000 --> 00:48:02.890 will store in self.passengers that is going to be equal to the empty list. 00:48:02.890 --> 00:48:07.110 So we start out with an empty list of passengers. 00:48:07.110 --> 00:48:09.770 So now if I want to create a flight, I can 00:48:09.770 --> 00:48:14.030 say flight equals and then Flight, that's the name of the class, 00:48:14.030 --> 00:48:15.620 and then provide a capacity. 00:48:15.620 --> 00:48:19.400 I can say capacity of three to mean three people can go 00:48:19.400 --> 00:48:21.345 in this flight, but no more than three. 00:48:21.345 --> 00:48:23.720 That is the capacity because that is the argument that is 00:48:23.720 --> 00:48:25.852 specified inside of this init function. 00:48:25.852 --> 00:48:27.560 And when I do so, I'm automatically going 00:48:27.560 --> 00:48:31.780 to get this empty list of passengers. 00:48:31.780 --> 00:48:36.090 So now let's think about what methods, or what functions, 00:48:36.090 --> 00:48:39.690 we might care about performing when it comes to a flight. 00:48:39.690 --> 00:48:42.920 So one reasonable function to add would be a function that says, 00:48:42.920 --> 00:48:45.010 all right, let's add a passenger to the flight 00:48:45.010 --> 00:48:48.640 if I want someone new to go on the flight. 00:48:48.640 --> 00:48:50.960 So how might it go about doing that? 00:48:50.960 --> 00:48:53.830 Well, let's define a new method, also known as a function, 00:48:53.830 --> 00:48:57.580 to this flight class called Add Passenger. 00:48:57.580 --> 00:49:00.010 This method can be called whatever we want. 00:49:00.010 --> 00:49:03.160 Because this is a method that's going to work on an individual object, 00:49:03.160 --> 00:49:05.530 we need some way of referencing that object itself. 00:49:05.530 --> 00:49:07.900 So we'll use the keyword self again. 00:49:07.900 --> 00:49:11.600 And when we add a passenger, we need to add a passenger by their name. 00:49:11.600 --> 00:49:17.530 So I need to specify their name as well, such that now here, I 00:49:17.530 --> 00:49:21.670 want to add that name to the passengers list. 00:49:21.670 --> 00:49:24.040 How do I get access to the passengers list? 00:49:24.040 --> 00:49:26.980 Well, I have access to the self, the object of itself. 00:49:26.980 --> 00:49:30.130 And I store the passengers inside of self, 00:49:30.130 --> 00:49:34.780 in self.passenger, an attribute of this object. 00:49:34.780 --> 00:49:39.200 And self.passenger is a list that initially starts out as an empty list. 00:49:39.200 --> 00:49:41.860 But if I want to add something to the end of the list, 00:49:41.860 --> 00:49:43.940 we've already seen that in order to do that, 00:49:43.940 --> 00:49:48.620 I can say self-passengers.append name. 00:49:48.620 --> 00:49:54.300 So that adds in someone new to the end of this passengers list. 00:49:54.300 --> 00:49:56.910 Now, what could potentially go wrong here? 00:49:56.910 --> 00:50:00.060 Well, every time we call this Add Passenger function, what's 00:50:00.060 --> 00:50:03.200 going to happen is we are going to append to the end of this passengers 00:50:03.200 --> 00:50:04.980 list this name. 00:50:04.980 --> 00:50:09.030 But we haven't taken into consideration the capacity of the flight. 00:50:09.030 --> 00:50:11.880 Ideally, our Add Passengers function shouldn't 00:50:11.880 --> 00:50:17.468 let someone be added to a flight if the flight is already at capacity. 00:50:17.468 --> 00:50:19.510 So there are a number of things we could do here. 00:50:19.510 --> 00:50:21.570 We could just check it inside of this function. 00:50:21.570 --> 00:50:25.290 But just for good measure, let's create a new function. 00:50:25.290 --> 00:50:29.790 Let's add a new function called Open Seats that 00:50:29.790 --> 00:50:34.600 is going to return the number of open seats that are on the plane. 00:50:34.600 --> 00:50:36.370 Other than Self, there are no other inputs 00:50:36.370 --> 00:50:38.710 that we need to calculate how many open seats there are. 00:50:38.710 --> 00:50:41.890 The only thing we need to know in order to calculate open seats 00:50:41.890 --> 00:50:48.290 is we need to know the capacity minus however many passengers there are. 00:50:48.290 --> 00:50:52.040 Remember, self.passengers is our list of all the passengers. 00:50:52.040 --> 00:50:55.100 And any time we have a sequence to get the length of that sequence, 00:50:55.100 --> 00:50:58.430 I can say len, or length of that sequence, 00:50:58.430 --> 00:51:02.490 to say get me the number of passengers that there are. 00:51:02.490 --> 00:51:05.060 So now we have this function called Open Seats, which 00:51:05.060 --> 00:51:08.330 will return capacity minus the number of passengers 00:51:08.330 --> 00:51:12.710 and tell us how many open seats there are. 00:51:12.710 --> 00:51:16.880 And now in this Add Passenger function, I can add some additional logic. 00:51:16.880 --> 00:51:22.160 I can say if not self.open_seats. 00:51:24.920 --> 00:51:27.470 So this is equivalent to me saying in this case, like, 00:51:27.470 --> 00:51:32.340 if self.open_seats equals equals zero, meaning there are no open seats, 00:51:32.340 --> 00:51:36.560 a more Pythonic way, so to speak, of expressing this idea is just saying 00:51:36.560 --> 00:51:38.180 if not self.open_seats. 00:51:38.180 --> 00:51:42.600 In other words, if there aren't any more open seats, then what should we do? 00:51:42.600 --> 00:51:43.850 We should return. 00:51:43.850 --> 00:51:47.420 And maybe you might imagine this Add Passenger function returns true 00:51:47.420 --> 00:51:51.330 if it was able to successfully add a passenger and false otherwise. 00:51:51.330 --> 00:51:54.080 So in this case, I can return false to say, you know what? 00:51:54.080 --> 00:51:55.460 There aren't enough open seats. 00:51:55.460 --> 00:51:57.950 Let me return false from the function to indicate 00:51:57.950 --> 00:52:00.550 that there was some sort of error. 00:52:00.550 --> 00:52:03.610 But otherwise, if there are open seats, we can add the passenger 00:52:03.610 --> 00:52:06.760 and return true to mean that everything was OK, 00:52:06.760 --> 00:52:10.867 we were able to add the passenger successfully. 00:52:10.867 --> 00:52:12.450 So now we have these three functions-- 00:52:12.450 --> 00:52:15.030 Init that creates a new flight, Add Passenger 00:52:15.030 --> 00:52:17.220 that adds a new passenger to that flight, 00:52:17.220 --> 00:52:20.560 and Open Seats, which tells us how many open seats there are. 00:52:20.560 --> 00:52:23.340 And now let's use those functions to actually add 00:52:23.340 --> 00:52:25.860 some passengers to this flight. 00:52:25.860 --> 00:52:27.240 Let me get a list of people. 00:52:27.240 --> 00:52:32.700 We'll say Harry, Ron, Hermione, and Ginny. 00:52:32.700 --> 00:52:35.340 And now let me loop over all of those people. 00:52:35.340 --> 00:52:42.554 For every person in that list of people, let's try to flight.add_passenger 00:52:42.554 --> 00:52:44.410 person. 00:52:44.410 --> 00:52:48.770 And we can save the result in a variable called success, for example. 00:52:48.770 --> 00:52:51.350 And then I can say if success, well, then 00:52:51.350 --> 00:52:59.460 let's print out that we added the person to flight successfully. 00:52:59.460 --> 00:53:07.740 But else, otherwise, let's print out no available seats for that person. 00:53:07.740 --> 00:53:08.990 So what's going on here? 00:53:08.990 --> 00:53:11.120 We have a list of people, four people. 00:53:11.120 --> 00:53:14.180 And for each of those people, we're going to try and add the passenger 00:53:14.180 --> 00:53:16.940 to the flight calling flight.add_passenger, 00:53:16.940 --> 00:53:20.450 calling this method, passing, as input, the person's name, 00:53:20.450 --> 00:53:24.440 and save the result true or false in this variable called Success. 00:53:24.440 --> 00:53:28.160 If success is true, we print out we've added them successfully. 00:53:28.160 --> 00:53:32.880 Otherwise, we print out there are no available seats for that person. 00:53:32.880 --> 00:53:35.840 So now we can try running this program. 00:53:35.840 --> 00:53:38.800 I'll run Python classes.py. 00:53:38.800 --> 00:53:41.800 And now we see we've added Harry, Ron, and Hermione to the flight 00:53:41.800 --> 00:53:42.790 successfully. 00:53:42.790 --> 00:53:45.400 But the flight had a capacity of three, which 00:53:45.400 --> 00:53:48.700 means there are no available seats for Ginny, which we get as the error 00:53:48.700 --> 00:53:50.587 message on the fourth line. 00:53:50.587 --> 00:53:52.420 But if you're really trying to optimize, you 00:53:52.420 --> 00:53:54.910 might notice you don't really need this variable. 00:53:54.910 --> 00:53:56.800 I could just take this entire expression, 00:53:56.800 --> 00:54:01.180 flight.add_passenger person, and put it in the condition itself. 00:54:01.180 --> 00:54:03.670 I can say, try and add a passenger. 00:54:03.670 --> 00:54:06.140 Add passenger will return true or false. 00:54:06.140 --> 00:54:09.130 And if it returns true, that means it was a success. 00:54:09.130 --> 00:54:12.460 And then I can print out that we've added the person to the flight 00:54:12.460 --> 00:54:14.430 successfully. 00:54:14.430 --> 00:54:17.130 So that is a brief look at object-oriented programming, 00:54:17.130 --> 00:54:19.800 this technique within Python and other programming languages 00:54:19.800 --> 00:54:23.040 to represent objects like this particular flight 00:54:23.040 --> 00:54:26.550 and then to manipulate those objects using methods like the Add Passenger 00:54:26.550 --> 00:54:29.490 method that takes a flight and adds people to it, 00:54:29.490 --> 00:54:32.640 at least as long as there is available capacity on that flight. 00:54:32.640 --> 00:54:34.860 It's one of the many powerful features of Python 00:54:34.860 --> 00:54:37.440 that we'll be definitely taking a look at later in the term 00:54:37.440 --> 00:54:41.522 and using as we go about building these web applications. 00:54:41.522 --> 00:54:43.230 Now, there are a couple of final examples 00:54:43.230 --> 00:54:45.438 that are just worth taking a look at just to give you 00:54:45.438 --> 00:54:49.350 some exposure to some of the other features that are available in Python. 00:54:49.350 --> 00:54:53.100 One thing that will be coming up soon is the idea of decorators. 00:54:53.100 --> 00:54:56.070 And just as we can take a value in Python like a number 00:54:56.070 --> 00:55:00.360 and modify the value, decorators are a way in Python of taking a function, 00:55:00.360 --> 00:55:04.870 and modifying that function, adding some additional behavior to that function. 00:55:04.870 --> 00:55:08.520 So I'll create a new file called decorators.py just 00:55:08.520 --> 00:55:10.830 to demonstrate what we can do with decorators. 00:55:10.830 --> 00:55:13.350 And the idea of a decorator is a decorator 00:55:13.350 --> 00:55:17.070 is going to be a function that takes a function of input 00:55:17.070 --> 00:55:20.910 and returns a modified version of that function as output. 00:55:20.910 --> 00:55:24.540 So unlike other programming languages where functions just exist on their own 00:55:24.540 --> 00:55:28.380 and they can't be passed in as input or output to other functions, in Python, 00:55:28.380 --> 00:55:30.420 a function is just a value like any other. 00:55:30.420 --> 00:55:32.610 You can pass it as input to another function. 00:55:32.610 --> 00:55:35.250 You can get it as the output of another function. 00:55:35.250 --> 00:55:37.980 And this is known as a functional programming paradigm, 00:55:37.980 --> 00:55:41.560 where functions are themselves values. 00:55:41.560 --> 00:55:46.470 So let's create a function that modifies another function by announcing 00:55:46.470 --> 00:55:49.950 that the function is about to run and that the function has completed run, 00:55:49.950 --> 00:55:51.270 just to demonstrate. 00:55:51.270 --> 00:55:55.310 So this Announce function will take, as input, a function f. 00:55:55.310 --> 00:55:57.780 And it's going to return a new function. 00:55:57.780 --> 00:56:01.020 And usually, this function wraps up this function 00:56:01.020 --> 00:56:03.960 f with some additional behavior, and for that reason, 00:56:03.960 --> 00:56:05.760 is often called a wrapper function. 00:56:05.760 --> 00:56:09.420 So we may call this wrapper to say that, all right, what 00:56:09.420 --> 00:56:11.160 is my wrapper function going to do? 00:56:11.160 --> 00:56:14.588 It's first going to print about to run the function just 00:56:14.588 --> 00:56:16.630 to announce that we're about to run the function. 00:56:16.630 --> 00:56:19.470 That's what I want my Announce decorator to do. 00:56:19.470 --> 00:56:22.110 Then let's actually run the function f. 00:56:22.110 --> 00:56:26.670 And then let's print done with the function. 00:56:26.670 --> 00:56:30.300 So what my Announce decorator is doing is it's taking the function f 00:56:30.300 --> 00:56:34.320 and it's creating a new function that just announces, via a print statement, 00:56:34.320 --> 00:56:37.560 before and after the function is done running. 00:56:37.560 --> 00:56:40.620 And then at the end, we'll return this new function, 00:56:40.620 --> 00:56:42.910 which is the wrapper function. 00:56:42.910 --> 00:56:45.970 So this right here is what we might call a decorator, a function that 00:56:45.970 --> 00:56:49.930 takes a function, modifies it by adding some additional capabilities to it, 00:56:49.930 --> 00:56:52.940 and then gives us back some output. 00:56:52.940 --> 00:56:56.800 So now here, I can define a function called Hello that just prints "hello, 00:56:56.800 --> 00:56:58.870 world," for example. 00:56:58.870 --> 00:57:01.940 And then to add a decorator, I use the at symbol. 00:57:01.940 --> 00:57:08.370 I can say at announce to say add the Announce decorator to this function. 00:57:08.370 --> 00:57:10.320 And then I'll just run the function Hello. 00:57:10.320 --> 00:57:12.340 And we'll see what happens. 00:57:12.340 --> 00:57:15.012 I'll run Python decorators.py. 00:57:15.012 --> 00:57:19.950 And I see about to run the function, then "hello, world," then done 00:57:19.950 --> 00:57:21.183 with the function. 00:57:21.183 --> 00:57:22.350 So again, why did that work? 00:57:22.350 --> 00:57:25.980 It's because our Hello function that just printed 'hello, world" 00:57:25.980 --> 00:57:28.770 is wrapped inside of this Announce decorator, 00:57:28.770 --> 00:57:32.760 where what the Announce decorator does, is it takes our Hello function of input 00:57:32.760 --> 00:57:36.450 and gets us a new function that first prints an alert warning that we're 00:57:36.450 --> 00:57:39.330 about to run the function, actually runs the function, 00:57:39.330 --> 00:57:41.310 and then prints another message. 00:57:41.310 --> 00:57:43.230 So, a bit of a simple example here. 00:57:43.230 --> 00:57:46.680 But there's a lot of power in decorators for being able to very quickly take 00:57:46.680 --> 00:57:49.170 a function and add capability to it. 00:57:49.170 --> 00:57:51.510 You might imagine in a web application, if you only 00:57:51.510 --> 00:57:55.420 want certain functions to be able to run, if a user is logged in, 00:57:55.420 --> 00:57:57.780 you can imagine writing a decorator that checks 00:57:57.780 --> 00:58:00.060 to make sure that a user is logged in, and then just 00:58:00.060 --> 00:58:03.450 using that decorator on all of the functions that you want to make sure 00:58:03.450 --> 00:58:06.400 only work when a user so happens to be logged in. 00:58:06.400 --> 00:58:09.660 So decorators are a very powerful tool that web application frameworks 00:58:09.660 --> 00:58:12.930 like Django can make use of just to make the web application development 00:58:12.930 --> 00:58:16.640 process a little bit easier as well. 00:58:16.640 --> 00:58:21.070 Let's take a look at a couple other techniques that exist within Python. 00:58:21.070 --> 00:58:25.940 One is how we might be able to more efficiently represent functions. 00:58:25.940 --> 00:58:27.550 So let's imagine that I now have-- 00:58:27.550 --> 00:58:31.610 I'm going to call this lambda.py for a reason you'll see in a moment. 00:58:31.610 --> 00:58:35.110 Let's imagine that I have a list of names or people, for example. 00:58:35.110 --> 00:58:39.790 And inside of this list of people, each person, instead of being just a string, 00:58:39.790 --> 00:58:43.570 is going to be a dictionary that has both a name like Harry 00:58:43.570 --> 00:58:46.810 and a house like Gryffindor. 00:58:46.810 --> 00:58:51.610 And let me add another name like Cho and a house like Ravenclaw, 00:58:51.610 --> 00:58:58.850 and then another name like Draco and a house like Slytherin. 00:58:58.850 --> 00:59:02.300 So here we have a list where each of the elements inside of that list 00:59:02.300 --> 00:59:04.490 is a dictionary, a mapping of keys and values. 00:59:04.490 --> 00:59:05.600 And that's totally OK. 00:59:05.600 --> 00:59:09.660 In Python, we have the ability to nest the data structures within one another. 00:59:09.660 --> 00:59:12.290 We can have lists inside of other lists, or lists inside 00:59:12.290 --> 00:59:15.860 of dictionaries, or in this case, dictionaries inside of a list. 00:59:15.860 --> 00:59:17.960 And in fact, this nesting of data structures 00:59:17.960 --> 00:59:20.420 is one of the reasons why it's very easy in Python 00:59:20.420 --> 00:59:23.780 to be able to represent structured data like a list of people 00:59:23.780 --> 00:59:26.900 where every person has various different properties. 00:59:26.900 --> 00:59:30.860 What I might like to do now is something like sort all of these people 00:59:30.860 --> 00:59:33.440 and then print them all out. 00:59:33.440 --> 00:59:39.570 So I might want to say people.sort, and then print all the people. 00:59:39.570 --> 00:59:42.730 But if I try to run this, I'll get an exception. 00:59:42.730 --> 00:59:49.350 I get an exception, type error less than not supported between dict and dict, 00:59:49.350 --> 00:59:52.990 which is sort of weird because I'm not using any less than symbol at all 00:59:52.990 --> 00:59:54.152 anywhere in a program. 00:59:54.152 --> 00:59:57.360 But in the trace-back, you'll see that the line of code that it's catching on 00:59:57.360 --> 00:59:58.950 is people.sort. 00:59:58.950 --> 01:00:02.070 Somehow, people.sort is causing a type error 01:00:02.070 --> 01:00:06.630 because it's trying to use less than to compare two dictionaries. 01:00:06.630 --> 01:00:09.090 And what this appears to mean is that Python doesn't 01:00:09.090 --> 01:00:11.790 know how to sort these dictionaries. 01:00:11.790 --> 01:00:14.280 It doesn't know, does Harry belong before or after Cho 01:00:14.280 --> 01:00:18.090 because it doesn't know how to compare these two elements. 01:00:18.090 --> 01:00:19.980 And so if I want to do something like this, 01:00:19.980 --> 01:00:24.480 then I need to tell the sort function how to sort these people. 01:00:24.480 --> 01:00:26.970 And so in order to do that, one way I could do this 01:00:26.970 --> 01:00:30.810 is by defining a function that tells the sort function 01:00:30.810 --> 01:00:33.480 how to do the sorting, what to look at when sorting. 01:00:33.480 --> 01:00:36.780 So if I want to sort by people's name, let me define a function 01:00:36.780 --> 01:00:39.750 that I'll just call f, that takes a person as input 01:00:39.750 --> 01:00:43.800 and returns that person's name by looking up the name field 01:00:43.800 --> 01:00:45.790 inside of the dictionary. 01:00:45.790 --> 01:00:50.640 And now I can sort people by their name by saying sort key equals f. 01:00:50.640 --> 01:00:52.680 What this means is sort all the people. 01:00:52.680 --> 01:00:55.590 And the way to sort them, the way you know how to compare them, 01:00:55.590 --> 01:00:59.670 is by running this function where this function takes a person 01:00:59.670 --> 01:01:01.330 and gives us back their name. 01:01:01.330 --> 01:01:03.580 And this will sort everyone by name. 01:01:03.580 --> 01:01:06.690 Now, if I run Python lambda.py, you will see that I first 01:01:06.690 --> 01:01:10.230 get Cho, then Draco, then Harry in alphabetical order 01:01:10.230 --> 01:01:14.490 by name, whereas if instead I had tried to sort people by their house 01:01:14.490 --> 01:01:19.020 by changing my function that I'm using to sort and then rerun this, now 01:01:19.020 --> 01:01:21.690 I see that its first Harry who is in Gryffindor, then 01:01:21.690 --> 01:01:24.320 Ravenclaw, then Slytherin, for example. 01:01:24.320 --> 01:01:28.230 So we get the houses in alphabetical order instead. 01:01:28.230 --> 01:01:31.560 But the reason I show this is because this function is so simple 01:01:31.560 --> 01:01:33.330 and is only used in one place. 01:01:33.330 --> 01:01:35.820 Python actually gives us an easier way to represent 01:01:35.820 --> 01:01:40.510 a very short, one-line function using something called a lambda expression. 01:01:40.510 --> 01:01:42.840 And this is a way of including the function just 01:01:42.840 --> 01:01:45.150 as a single value on a single line. 01:01:45.150 --> 01:01:49.500 I can say instead of defining a function called f, I can get rid of all of this 01:01:49.500 --> 01:01:53.110 and just say, sort by this key, a lambda, 01:01:53.110 --> 01:01:59.080 which is a function that takes a person and returns the person's name. 01:01:59.080 --> 01:02:03.760 So we say person as the input, colon person name as the output. 01:02:03.760 --> 01:02:06.090 This is a condensed way of saying the same thing we 01:02:06.090 --> 01:02:09.390 saw a moment ago, of defining a function, giving it a name, 01:02:09.390 --> 01:02:11.070 and then passing in the name here. 01:02:11.070 --> 01:02:15.840 This right here is a complete function that takes a person as input 01:02:15.840 --> 01:02:17.850 and returns their name. 01:02:17.850 --> 01:02:22.350 So Python lambda.py, that will actually sort the people by their name-- 01:02:22.350 --> 01:02:24.870 Cho, then Draco, then Harry. 01:02:24.870 --> 01:02:29.580 Whereas if I have left off this key altogether and then tried to sort, 01:02:29.580 --> 01:02:30.990 well then we get this type error. 01:02:30.990 --> 01:02:34.140 Because we can't compare these two dictionaries. 01:02:34.140 --> 01:02:37.110 So we've seen a lot of different exceptions now throughout Python. 01:02:37.110 --> 01:02:39.240 So the very last example we'll take a look at 01:02:39.240 --> 01:02:41.670 is an example of how to deal with these exceptions, 01:02:41.670 --> 01:02:45.000 like what to do when things might go wrong if we want our program 01:02:45.000 --> 01:02:49.860 to be able to handle those possible exceptional cases, situations where 01:02:49.860 --> 01:02:52.660 things might, in fact, go wrong. 01:02:52.660 --> 01:02:53.880 So let's try an example. 01:02:53.880 --> 01:02:57.630 I'll create a new file that I'm going to call exceptions.py. 01:02:57.630 --> 01:03:01.860 And what exceptions.py is going to do is it's going to get some input. 01:03:01.860 --> 01:03:05.970 It's going to say let's get an integer input called x. 01:03:05.970 --> 01:03:09.150 And let's get an integer input called y. 01:03:09.150 --> 01:03:13.200 And then it lets go ahead and print out the result of x divided by y. 01:03:13.200 --> 01:03:15.960 So result equals x divided by y. 01:03:15.960 --> 01:03:23.980 And then let's print out something like x divided by y equals result. 01:03:23.980 --> 01:03:28.022 And we can literally print out the values of x and y. 01:03:28.022 --> 01:03:30.730 So this is a simple program that's just performing some division. 01:03:30.730 --> 01:03:34.030 Get a value of x, get a value of y, divide the two, 01:03:34.030 --> 01:03:37.630 and print out the result. We can try running this by running Python 01:03:37.630 --> 01:03:41.980 exceptions.py if I would type in like five and then 10, 01:03:41.980 --> 01:03:44.470 five divided by 10 is 0.5-- 01:03:44.470 --> 01:03:46.620 exactly what I might expect. 01:03:46.620 --> 01:03:48.080 But what could go wrong now? 01:03:48.080 --> 01:03:51.850 You remember from math in division, what could go wrong is if I type in five 01:03:51.850 --> 01:03:55.720 and then zero, try and do five divided by zero, what's going to happen? 01:03:55.720 --> 01:03:57.610 Well, when I do that, I get an exception. 01:03:57.610 --> 01:04:00.310 I get a zero division error, which is an error that happens 01:04:00.310 --> 01:04:02.898 whenever you try to divide by zero. 01:04:02.898 --> 01:04:04.690 What I'd like to happen though in this case 01:04:04.690 --> 01:04:08.170 is not for my program to display kind of a messy error and a trace-back 01:04:08.170 --> 01:04:11.560 like this, but to handle the exception gracefully, so to speak. 01:04:11.560 --> 01:04:15.400 To be able to catch when the user does something wrong 01:04:15.400 --> 01:04:18.670 and report a nicer looking message instead. 01:04:18.670 --> 01:04:20.540 And so how might I go about doing that. 01:04:20.540 --> 01:04:25.255 Well, one thing I can do here is instead of just saying result equals x over y, 01:04:25.255 --> 01:04:32.020 I can say try to do this, try to set result equal to x divided by y, 01:04:32.020 --> 01:04:36.790 and then say except if a zero division error happens. 01:04:36.790 --> 01:04:38.240 Then let's do something else. 01:04:38.240 --> 01:04:44.713 Let's print error cannot divide by zero, and then exit the program. 01:04:44.713 --> 01:04:45.880 How do you exit the program? 01:04:45.880 --> 01:04:49.420 It turns out there's a module in Python called sys. 01:04:49.420 --> 01:04:55.170 And if I import the sys module, I can say sys.exit1 01:04:55.170 --> 01:04:58.990 to mean exit the program with a status code of one, where a status code of one 01:04:58.990 --> 01:05:02.470 generally means something went wrong in this program. 01:05:02.470 --> 01:05:05.350 So now I'm trying to divide-- x divided by y-- 01:05:05.350 --> 01:05:08.170 except I have an exception handler. 01:05:08.170 --> 01:05:09.790 This is a try-except expression. 01:05:09.790 --> 01:05:13.540 I'm saying try to do this except if this exception happens, rather than have 01:05:13.540 --> 01:05:17.980 the program crash, just print out this error message, can't divide by zero, 01:05:17.980 --> 01:05:20.280 and then exit the program. 01:05:20.280 --> 01:05:21.805 So now let's try it-- 01:05:21.805 --> 01:05:22.680 Python exceptions.py. 01:05:22.680 --> 01:05:27.880 Again, five and 10 works totally normally, gets me a value of 0.5. 01:05:27.880 --> 01:05:31.920 But now if I try five and zero, press Return, I get an error. 01:05:31.920 --> 01:05:33.165 Cannot divide by zero-- 01:05:33.165 --> 01:05:35.790 no long exception that's going to look complicated to the user. 01:05:35.790 --> 01:05:37.020 It's no longer messy. 01:05:37.020 --> 01:05:40.350 I've been able to handle the exception gracefully. 01:05:40.350 --> 01:05:43.980 Now, one other exception that might come up is what if instead of x is five, 01:05:43.980 --> 01:05:48.000 I type in a word like "hello," something that's not a number. 01:05:48.000 --> 01:05:51.970 Now I get another type of exception, a value error, 01:05:51.970 --> 01:05:55.060 which is happening when I try and convert something to an int 01:05:55.060 --> 01:05:58.740 because Hello cannot be converted into a base 10 integer. 01:05:58.740 --> 01:06:02.230 You can't take text that is not a number and turn it into an integer. 01:06:02.230 --> 01:06:05.610 So instead, I'm getting this value error here. 01:06:05.610 --> 01:06:06.783 How can I deal with that? 01:06:06.783 --> 01:06:08.700 Well, I can deal with it in much the same way. 01:06:08.700 --> 01:06:13.080 When I'm getting this input x and y, I can say rather than just get the input, 01:06:13.080 --> 01:06:16.030 just try to get the input. 01:06:16.030 --> 01:06:20.020 Except if a value error happens, which is the area that we got a moment ago, 01:06:20.020 --> 01:06:25.660 this value error, then print error invalid input, 01:06:25.660 --> 01:06:28.890 and go ahead and sys.exit1. 01:06:28.890 --> 01:06:31.050 So now I've been able to handle that error as well. 01:06:31.050 --> 01:06:33.390 I can say Python exceptions.py. 01:06:33.390 --> 01:06:35.280 I can say Hello. 01:06:35.280 --> 01:06:37.170 And I just get error invalid input. 01:06:37.170 --> 01:06:38.580 I can divide by zero. 01:06:38.580 --> 01:06:40.620 I get error cannot divide by zero. 01:06:40.620 --> 01:06:43.120 But if I do type of valid x and a y value, 01:06:43.120 --> 01:06:46.840 then I get the result of dividing one number by the other. 01:06:46.840 --> 01:06:49.050 So exception handling is often a helpful tool 01:06:49.050 --> 01:06:51.810 for if you expect that some lines of code you might be running 01:06:51.810 --> 01:06:55.110 might run into some sort of problem, be they a value error, or a zero division 01:06:55.110 --> 01:06:57.180 error, or some other error altogether, to be 01:06:57.180 --> 01:06:59.100 able to handle those errors gracefully. 01:06:59.100 --> 01:07:00.892 And that's probably what you want if you're 01:07:00.892 --> 01:07:03.240 going about building a web application using Python, 01:07:03.240 --> 01:07:05.737 is the ability to say that if something goes wrong, 01:07:05.737 --> 01:07:08.070 we want to handle the error nicely, display a nice error 01:07:08.070 --> 01:07:10.950 message to the user telling them what was wrong instead 01:07:10.950 --> 01:07:13.613 of having the program entirely crash. 01:07:13.613 --> 01:07:16.530 So those are some of the key features now with this Python programming 01:07:16.530 --> 01:07:19.110 languagem this language that gives us the ability 01:07:19.110 --> 01:07:22.650 to define these functions, and loops, and conditions in very convenient ways, 01:07:22.650 --> 01:07:26.520 to create classes where we can begin to build objects that are able to perform 01:07:26.520 --> 01:07:28.260 various different types of tasks. 01:07:28.260 --> 01:07:32.580 And next time using Python, we'll be able to design web applications such 01:07:32.580 --> 01:07:35.310 that users are able to make requests to our web applications 01:07:35.310 --> 01:07:37.720 and get some sort of response back. 01:07:37.720 --> 01:07:40.370 So we will see you next time.