[MUSIC PLAYING] DAVID MALAN: All right, this is CS50, Introduction to Programming with Python. My name is David Malan, and this is our week on library. So libraries are generally files of code that other people have written that you can use in your own programs or a library's code that you've written that you can use in your own program, but maybe not just this program, but another and another as well. So Python supports exactly this idea. This ability to share code with others, share code across your own projects. And it does so by way of what it calls module. A module in Python is just a library that typically has one or more functions or other features built into it. Generally, the purpose of a library or a module specifically is to encourage reusability of code if you find yourself using the same types of functions again and again, the same functionality. If you find yourself copying and pasting from an old project into your new project, odds are there's an opportunity there to factor out that code that you keep copying and pasting that you keep reusing and put it into a library that you can then load into your programs moving forward so as to not just copy and paste it and have all these different copies all over. So what are some of the modules or libraries that Python comes with? Well, Python comes with a random library literally, which is to say that when you install the Python interpreter on your Mac or PC or somewhere in the cloud, not only do you get Python, you get a whole bunch of modules as well. Now, these modules provide you with functions that you don't have access to just by default like you do print and input. Print and input and other such functions just work in Python. But sometimes functions are tucked away in these modules, so you have to be more deliberate about loading them into the computer's memory. So somewhere on the computer's hard drive, once you've installed Python, there is also, it turns out a file, probably called random.py that someone else wrote probably long ago but that you have access to. And in that random.py file, there's probably one or more functions that you yourself can use in order to do things randomly. That is to say how could you flip a coin in a program in Python? How could you pick a random number between 1 and 10 in Python? Well, you need a bit of randomness. And while you could figure out mathematically how to write functions like that yourself, it's a lot easier to stand on the shoulders of others who've already solved that problem for you so you can focus on the problem that you yourself want to solve. So for documentation on most any Python module, you go to the official Python docs. And you go to a URL like this where the documentation for that specific module lives. And within the documentation, you'll see a list of the functions or other functionality that some module provide. But how do you go about loading a module into your own program so that you can use the functions in that module? Well, we need a new keyword in Python and namely it's import. The import keyword in Python allows you to import the contents of the functions from some module in Python. Well, how might I go about using this in practice? Well, let me propose that there exists in that random module this function among others. So I have copied and pasted from the documentation this summary of a function called Choice. Now, the function exists in the random module, so to speak, not a random module, the random module. And so generally the documentation describes it fully like this. random.choice is how you would technically call this function, though, we'll see alternatives to that. In parentheses, there is a parameter called S-E-Q for sequence. And sequence generally means a list or something that is list-like if you have a list of numbers or strings or anything else. And the documentation elaborate. Well, how can I go about using this function to solve, perhaps, a familiar problem? Well, let me go ahead and open up VS Code here. And let me propose that we implement a program that simulates flipping a coin. A coin that in the US has heads or tails, the idea of which is to pick a decision with 50/50 probability. 50% probability of heads, 50% probability of tails. Or you can use some other mechanism like that. Well, let me go ahead and open a program with code called generate.py because I want to start generating a whole bunch of random information. The first of which is just going to be a coin toss. Now, how do I go about using that function? Well, I first have to import the random library. So literally the first or among the first lines of my file should be import random. And that just gives me access to all of the functions in that specific module. Now, suppose I want to flip a coin. Well, I can do random.choice per the documentation a moment ago. And that again takes a sequence. What's a sequence? It's a list or something that's list-like. And we know about lists. We've used lists to iterate over numbers. We've used lists to iterate over students at Hogwarts. Let's go ahead now and iterate over just a list of two sides of a coin, "heads," quote, unquote, or tails. Now, I could call these anything I want. These are my string. I just want to simulate a tossing a coin. So I'm just going to say in all lowercase, heads and tail. But notice the syntax. I have "heads" and "tails" in double quotes. That's because they're strings. I could also use single quotes so long as I'm consistent. There's a comma between them, which means the list has two elements. They're square brackets to the right and the left, which indicates that this is indeed a list. That's the syntax recall for defining a list in Python. And then lastly, there's something more familiar. There's the parentheses outside of those square brackets. But those are just the parentheses that belong to the Choice function and specify where its parameter gets passed in. But again, unlike past function, I have to specify what module this function is in at least for now. And so I do random.choice to call the specific function. All right, well, it's one thing to flip a coin picking between those with 50% probability. And that's what random.choice does. It takes in a list, and it returns to one of those values randomly with equal probability. Because I've passed in two items, I've got a 50/50 chance. If I passed in three items, it'd be a 33% chance for each of those items and so forth. Python does the math for you. But I want to store the value of this in a variable. So let's define a variable called coin equals whatever the return value is. So this is indeed like flipping a coin. I'm going to store in a variable called coin, whatever that value is, heads or tails. And now, just so I can see what's going on, let's go ahead and print out the value of that string coin All right, let me go ahead now and run this program in my terminal window. Python of generate.py, Enter. And it looks like the first coin toss was the heads. Let's go ahead and run it again. And it looks like it was heads again. Maybe you want to chime into the chat here. If I run it a third time, what's it going to be this time? If you want to type your thoughts in the chat, you might think there's a bug here. But this is probability in action. If I go ahead and hit Enter a third time there, it's actually now tails And again, tails and again tails and again tails and again tails and again heads. Now, if we did this an infinite number of times, it would indeed work out to be 50/50. If we only do it a few times, it might not work out as cleanly. But that's how probabilities indeed work. All right, so I've got that now working. Could I have implemented this in a different way? Well, let me show you an alternative to actually using the import keyword alone and let me introduce the keyword from in Python. So from is a keyword in Python that you can use when importing functions from a module, but it allows you to be a little more specific than import alone. So if I go back to my code here, it's worth noting that what technically I'm doing here by importing random is I'm technically importing everything that's in that module. So not just the function called random.choice but a few other functions as well. So instead of using this line of code at the top of my file, import random, which will technically give me access to all of the contents they're in, a downside of that is that I have to type in random.choice, random.this, random.that because all of the functions I'm calling have to be associated with the scope of that module. Well, suppose that I just want to call the function as its name, choice. I can do that as well. Let me replace this first line here with from random import choice. And what this does effectively is it loads the function's name choice into my current namespace into the scope of the file I'm working in. What that means is that I now no longer have to specify which choice function I mean. I can just say choice. And so it loads it into the local namespace that is into my local vocabulary, if you will, so I can just now say choice. This might be advantageous in what cases, do you think? When might you want to import the name of the function explicitly like this as opposed to just saying random.choice-- random.choice throughout your code when calling a function? Any instincts here for this alternative import using from? AUDIENCE: Hello, I'm Mohammed Omar from Egypt. And maybe if we have a variable that its name is basically like choice if I have a variable called the choice. So I need to differentiate which trays I choose. So I'm going to choose random data choice. DAVID MALAN: Yeah, really good instincts. By using the first approach by just importing random, you're making sure that all of its contents are associated with or scoped to the random module so that you can have your own choice function. You can have your own choice variable. You can use the same names as all of the functions or variables that are stored inside of that file without them colliding, so to speak. And this is a good thing. In older languages, it was the case that if you imported someone's library, you better hope that you're not using the same functions or variables as they are because you might in fact have some kind of conflict. Python and certain other languages allow you to scope the names of those functions and variables to the file or the module that they come from. So that's a good thing. But honestly, this is such a short program. Or equivalently, maybe I'm using the choice function in so many places calling random.choice, random.choice, random.choice. It's just making my code longer and longer and longer. Marginally so, but hey, just getting ugly and annoying, I can simply import choice and now tighten up my code a little bit. So as with so many decisions in the past, there's not necessarily one right approach or another. It depends. But I think for those very reasons, sometimes it's better to do what we did the first time, which is only import the module so as to retain the scope therein. Well, let me propose that we transition to another function that comes with Python's random module. And that's this here from the documentation randint. It's a bit hard to say, but it implies get back a random int. And if you read the documentation, it's a random int that's between A and B inclusive. So if you were to pass in 1 for A and 10 for B, you would get back a number between 1 and 10 inclusive, including the 1 and including the 10 potentially. Each with a 10% probability. So how might I go about using a program like this? Well, let me come back to my generate the py file. And why don't we go ahead and try generating a random number between 1 and 10? You might do this frequently in the real world. When you just want someone to pick a random number, you tell them as much in the human response. Let's get the computer to do the same here. Let me go ahead and delete my two lines of code at the bottom but keep my import random. And let's go ahead and define a variable this time called number, set it equal to the return value of random.randint and now passing A, a value of 1 and B, a value of 10. And now, let's go ahead and print the number. I'm going to go ahead in my terminal window and run Python of generate.py and hit Enter. 4. A Python of generate.py and hit Enter. 8, again, 9, again, 7, again, 10, again, 2, again. And we can do this all day long. And if we add all of those up, they should end up being with 10% probability each. Now, how might you use this information? Well, maybe we're playing a guessing game. Or maybe we're trying to randomize the behavior of some character in the game. You can imagine using very simple building blocks like this just spicing up your program by getting it to do things a little less predictably because you're choosing these values seemingly randomly. And you're deferring to Python to actually do the generation of these numbers using its own algorithms and its own math. Well, what more could we do here? Let me propose that we introduce another function that comes from this random library. Yet, another that you yourself don't have to implement, shuffle. If you read the documentation for shuffle in the same random module, you'll see that it takes in a list, for instance, of values and just shuffles them up. It randomize them like a deck of cards. Here, you might shuffle them so as to put them into seemingly random order. Well, how do I use this based on this function's name? Well, let me propose that we go back to VS Code here. And let me go ahead and this time do the following. Because I need to shuffle something like a deck of cards, let me go ahead and not just import random. But let me give myself a variable called cards that's going to be of type list. And just so I have something to shuffle, I don't need all 52 cards in a typical deck. I'm just going to shuffle three cards. A Jack, a Queen, and a King. I could call those strings anything I want, but I just wanted a list of some values so as to shuffle them up. That is randomize the order therein. Well, how does this now work? If you read the documentation for random.shuffle, you'll see that it shuffles the argument in place. That is unlike many of the functions we have seen. It doesn't return to you a value that contains the shuffled cards in this case. It actually shuffles the list it's given itself. So what this means for my code is that I need to do something like this-- random.shuffle and pass in the variable containing those cards. And then on a final line here, how might I go about printing the cards? Well, I could do this, and I could say print card. But if I do that, I'm actually going to see Python syntax for lists. And it's just going to format in its own way using commas and the like. I want to print these cards out one at a time just because I think it'll look a little better so we can use some of our syntax from loops and say something like this-- for card in cards, go ahead and print out the current card. So what's now happening here? Line three, I'm defining a list of three cards in this order-- Jack, Queen, King. I'm then shuffling those same cards on line four. And then on line five, I'm using a for loop for each of the cards in that list printed out one at a time and because I'm using print one line at a time. Well, let's see the results. Down here in my terminal window, I'm going to run Python of generate.py and hit Enter. Queen, King, Jack seemingly shuffled because that's not the order I defined earlier. Let's do it again. Queen, King, Jack. Hmm, OK, that happens to be the same. But let's see. This could just be bad chance. There we go. Jack, Queen, King. Doesn't look like it's shuffled, but at least we're getting back different orderings now. Again, Jack, Queen, King. Hmm, not so good. Jack, Queen, King. Not so good. This is someone you probably want to play against with cards. Queen, Jack, King, there we go. But of course, we only have three cards here. So there's not that many permutations we might see. And if we do this over time, we will see all of them. But if we had, of course, 13 or 52 cards, we'd see a lot more permutations instead. So we have now these three ways to generate random information. One, a simple coin toss if you want to start some kind of athletic event. One, pick a number between 1 and 10 if you want to decide something based on that. And now, using shuffle, we can even take in a list of things and shuffle them about so that we get some kind of random behavior. Well, let me pause here and see if where there's any questions yet on random, on modules, or any of these three functions. AUDIENCE: Yeah, can we increase or decrease the probability of cards if we want to? For example, there are three. There is a 33% chance of probable B. So is there any chance to increase or decrease the probability? DAVID MALAN: Can you set these probabilities not using these same functions? Can you set the probabilities? But you can absolutely implement some of your own functions or use more sophisticated functions that do exist in this library and others to exercise more control. These are meant to be very user-friendly and simple functions, certainly the ones we looked at, that give you equal probability for all of those. But absolutely you could skew things, though, hopefully, if you're implementing a gambling game or the like, you're not actually making some cards more probable than others. Allow me to turn back now to our implementation here of this randomness and consider how we might leverage other types of functionality that aren't necessarily in this specific library here. Well, it turns out that Python also comes with a statistics library. And this contains all sorts of functions for doing things more statistical in nature, namely calculating means or medians or modes or other aspects of a data set that you might want to analyze. So how might we use the statistics module in Python? Well, we might first just take a look at it's documentation like any other module in Python. And we'll see within that library that there's a whole bunch of function. And one of those functions is one that's quite simple. It's average. A function that allows you to calculate the average of some numbers that you've passed in. Let me go ahead and in VS Code in my terminal window, open up a new file called average.py. And at the top of this file, I'm going to import a different library this time, namely the statistics module in Python. And now, I'm going to go ahead and call a function that I know comes in that module, namely mean for the average of some values. And I'm going to call statistics.mean. And I'm going to pass into this function mean, a list of some values. And let's suppose that I'm quickly trying to calculate what my current grade average is in school. And I did really well on my first test. And I got 100%. And on my second, I did well but not as well. And I got a 90. And ironically, I'm not very good with math. So I'd like to figure out what my average now is between those two tests. So let me go ahead now and in this list, type in the number 100, comma, 90, thereby passing in a list of two values, two INTs, 190. And outside of those are the parentheses because, of course, this is now the argument I'm passing to the function called mean. And this function mean is in the module called statistics. Well, it's not that interesting to just calculate the mean if I don't actually see what it is. So let me additionally pass the return value of that mean function to the print function as usual. Let me now in my terminal window in VS Code, type in Python of average.py and hit Enter. And voila, as you might expect, my average is 95%. So the difference here is that I'm just using a different module that still comes with Python. But I need to import it instead of, for instance, the random module instead. And this time, I know from the documentation that there exists a function called mean. Well, it turns out there's even more functionality that comes with Python and that comes with other modules in Python. And there's this feature generally known as command line arguments. This is a feature, not just of Python, but of languages more generally that allow you to provide input not when prompted inside of a program as happens whenever we call the Python function input. But rather, there's this feature, command line arguments of programs, that allows you to provide arguments that is input to the program of just when you're executing at the command line. So up until now, for instance, recall that we've generally run Python of something.py. For instance, Python of hello.py. And I've never once really executed any words or phrases after the name of the file, but I could. In fact, when you're running programs in a command-like environment like we are, you can provide any number of words or numbers or phrases after the command that you're typing. And all of those will somehow be passed in as inputs to the program itself. You don't have to prompt the user for one thing at a time by manually calling that input function. So what does this mean in real terms? Well, let me go ahead back into VS Code here. And let me propose that we consider how we might leverage a certain module. I'm going to go ahead and create a file called name.py. And I'd like to use a new module this time that's going to give me access to values that have been typed at the command line. But what's this module going to be? Well, this one's going to be called sys. And sys, short for system, contains a whole lot of functionality that's specific to the system itself and the commands that you and I are typing. The documentation for this module is at this URL here. And it lists all of the various functions and variables and the like that come with that module. But we're going to focus on something a little more specific, namely this thing here. It turns out in the sys module in Python, there is a variable that just magically exists for you called argv. It stands for argument vector which is a fancy way of describing the list of all of the words that the human typed in at their prompt before they hit Enter. All of those are seemingly magically provided to you via Python in a variable called sys.argv. This variable is a list, which means that the first element is going to be the first word that you type. The second element is going to be the second word that you typed in, so forth. And by way of this list, then, can you figure out what words did the human actually type at the prompt and maybe use that to influence the behavior of your own program? So what does this mean now in real terms? Well, in this new tab called name.py, let me go ahead and import sys. Within that sys module is going to give me access to sys.argv, but how might I want to use it? Well, let's do this. Instead of writing a Hello World program that all of these times has just looked for the return value of input to figure out what the user wants me to print, let's go ahead and just expect the user to tell us when they run the Python program itself, what their name is. And suppose this time, I'd like to generate a whole bunch of name tags, initially just one. And in the US here, it's very common to wear a sticker on your lapel that says Hello, my name is David. So I want to print out some text that resembles that. The idea being maybe I could enhance this program someday to even send that text straight to the printer and dynamically generate those name tags. Well, let me go ahead now and do this. Let me go ahead and print out as always, Hello. But I'll say a little something more this time to make things more interesting. Hello, my name "is," quote, unquote. And then after that, I normally have been in the habit of calling input, storing the return value in a variable, and passing in the name of that variable here. But I'm going to instead jump right to this-- sys.argvbracket1. And that's it. I'm going to have a program here that says Hello, my name is followed by whatever is in sys.argvbracket1. And notice, this .argv again is a list. And recall from our discussion of loops and in turn list, we use this square bracket notation to get at the various elements inside of a list. All right, let me go down now into my terminal window and run Python of name.py. But this time, rather than just hit Enter and wait for the program to prompt me for my name, let me proactively just tell this program what my name is at the so-called command line. Here we go. D-A-V-I-D separated with a space from the name of the file so that now when I execute Python, name.py David, I see on the screen, voila, Hello, my name is David. So based on this demonstration alone, I think we can infer exactly what's going on in sys.argv even though it sounds certainly at first glance, rather complicated here. Let's look up. At sys.argv, I'm going to bracket1 here. So clearly, sys.argvbracket1 is storing D-A-V-I-D. But it's one. In the past when we looked at loops, recall that we said that they were zero index. That is the first element is zero. The next element is one. This next element is two and so forth. And yet, here I am treating it as though my name is at the start of the list one. Well, let me ask this question, what is probably in sys.argv of 0? What is probably in sys.argv of 0-- the very first element actually in that list? AUDIENCE: Oh, yeah. I think it's like in C, the name of program. DAVID MALAN: Indeed, it's indeed like in C. Another language is the name of the program. Well, if we consider what it was I typed, I certainly typed Python because that's the name of my interpreter. And we don't really need to know that because we're using Python itself. But after that, I did type two things. I typed name.py as I've done so many times any time I want Python to the interpreter program I've written. And it turns out by convention, what Python does is it stores in sys.argv the name of the file that you're executing or interpreting followed by any number of other words that you type. So all this time, we could have been accessing the name of the program, which frankly, isn't all that interesting. But we can also now access words that are typed after that prompt as well. But of course, if I don't type anything in, what might happen here? This might be naive of me to assume that there's always going to be something at location1 in sys.argv. Let me go ahead and try this. Python, name.py. And I'm not giving you my name because at this point, I might not even know that you want my name to be typed. So let me hit Enter now. And uh, oh, we see now an error. A so-called exception in Python, this one's a new one. This one's an index error that elaborates list index out of range. And turns out this is actually one of the most common mistakes in programming, whether you're using a list in Python or arrays or vectors in other languages, is to try to access some element that does not exist. You try to go too far to the left. Or you try to go too far to the right in this object that is just a list of some values. So of course, the mistake here is that I'm assuming there's going to be something at location1 when really, it's location0. That's the only one that has a value. But fixing this is not going to amount to doing bracket0 because now if I go ahead and rerun this program with no other words after name.py, it says Hello, my name is name.py, which is fine if we're making a name tag for the program, but that's not, of course, what my goal here is instead. So if the fix is not just to change the one to a zero, how else might I handle this error? How else might I handle this error? This index error that happens if the user just doesn't remember to or doesn't know to type their actual name at the prompt. AUDIENCE: We could always put an exception into the program, say, if there's nothing at location1, we just come out and say, OK, we haven't got parameter or something. But if there is, we continue along with the program. DAVID MALAN: Perfect. So if I might simplify, we can try to execute this line of code, except if there's an error, we'll deal with it in some other way. Now, ideally-- and once I'm a strong enough programmer, I would have anticipated this and written the following code from the get go. But when you're learning, it's certainly reasonable to see an error. Oh, I didn't realize I should detect that and then go back and improve your code. But of course, if you read the documentation, you ingrain some of the lessons learned from the past. You'll get into the habit of trying and checking for some of these exceptions yourself. So let me solve this in one possible way as you've proposed here. Let's try to handle this exception as follows. Let me go ahead now. And instead of just blindly calling this print line, let me try to print out Hello, my name is such and such, except if there is an issue, specifically an index error, then what do I want to go ahead and do? I'm going to say something like too few arguments. I could be more explanatory than that. But for now, I'm just going to explain to the user that they gave me too few arguments, too few words at the prompt. So now, it's still not going to work in quite the way I want. I'm still not going to be able to generate their name tag. But at least, they're not going to see some cryptic error message and think that they themselves broke the program. Let me go ahead now and run Python of name.py Enter and too few arguments. OK, let me go ahead now and do Python of name.py and type in my name, David. And now we're back in business. And I see that my name is on the screen too. But strictly speaking, I don't have to try to do this. I could actually be a little more defensive in writing this code. And maybe I could check whether or not the user has indeed provided a name or multiple names at the prompt so as to give them more refined error messages as well. So how might I do this? Well, me go and undo the exception handling I've added. And why don't I instead more modestly try to do this? Let me go ahead and introduce a conditional here. If the length of sys.argv is less than 2 or equivalently equal to just one value-- but I'll just stick with less than 2 for now, then go ahead and print out two few arguments. So I want ultimately two arguments. I want the name of the program at location0. And I want the name of the human at location1. So that's a total of two arguments. So if I have fewer than two arguments, let's tell the user with this print line, L if the length of sys.argv is say greater than 2, like they typed in too many words at the prompt, well, let's tell them, print, quote, unquote, "too" many arguments. Else if they did get it right, and they gave me exactly two arguments. Else, let's go ahead and print what I actually care about. All right, let me go down to my terminal window here and run Python of name.py and voila. Uh, oh, a completely different type of error. This one a syntax error, which we've seen in the past. Now, a syntax error recall is mea culpa, like, I messed up here. And I wrote invalid syntax. And so no amount of conditionals or exception handling's really going to catch this one. I need to go back and just get my program to work because it's not running at all. Well, let me go up here and see. Line four is the issue. And indeed, it looks like I have an unterminated string here. I need to go ahead and now add this double quote. So let me go ahead now. And with that red herring gone, let me rerun Python of name.py and hit Enter. And now, we see too few arguments. OK, maybe it wants my full name. Let me go ahead now and run Python of name.py, David Malan, typing in both words after the name of the file and hit Enter. And now, of course, it's too many arguments. Fine. Now, I'll oblige and do Python of name.py and just David. And there we have it. My name tag printed on the screen. So strictly speaking, we don't have to handle exceptions if we can be a little smarter about it and just check for the things that we're worried about, especially if we want to give the user more refined advice. We don't want to just tell them no, something went wrong or we don't want to pass. We want to tell them no, that's too few or no, that's too many. We have conditionals in our vocabulary already via which we can now express that. Well, let me pause here and see if there's any questions now on how we handled the error before with the index error or how now we're just proactively avoiding all index errors altogether by just checking first, is it too few? Is it too many? Or is it exactly what we want? AUDIENCE: Hi, yeah, thank you. So I was wondering, you touched upon using your full name. Is there a way going forwards that perhaps we have people that want their full names and want just their first name that we separate that into, oh, this person has full name. This person has just the one name? DAVID MALAN: Absolutely, and allow me to propose we come back to that support for multiple names. But indeed, we could do that. And I should note too, though, we can support full names right now if I do this. Instead of typing in David space Malan, which is problematic because again, by definition of how are argv works, each word ends up in a specific location in the list. But if I add quotes, single quotes or double quotes at the command line, now, Python will view this as two total things. The name of the file and this full name. And now, when I hit Enter, I don't see the quotes. The whole thing is passed in as my full name. And if I want to adapt this further for multiple people, we'll be able to do that as well. Other questions now on this version with if, elif, else, or on except before. AUDIENCE: Python. I want to ask you, can we use multiple else's statement? DAVID MALAN: Can you use multiple else's statements? No, else is the last catchall statement that you can have. You can have multiple elif statements in the middle but not multiple elses. AUDIENCE: [INAUDIBLE] DAVID MALAN: All right. All right, well, let's turn our attention back now to this code and see if we can't refine it a bit more by adding in some additional functionality that we get with modules like the sys module. One of the things I don't love about this version of the code even though arguably it is now correct is that the essence of my program, which is just to print out the name tag, is relegated to this else clause. And that's fine. Logically, it's correct, but generally speaking, there is something nice about keeping all of your error handling separate from the code that you really care about having all of these ifs, elifs, perhaps at the top of your code that are checking to make sure that all of the data's as expected. But then it would be nice if only for design sake not to hide in this else statement the actual code that you care about. I would prefer, for instance, to do something logically like this. I could check for errors up top. And then down here, print the name tag. It would be nice if those are distinct blocks of code all of which are here left aligned. But there's a problem with what I've just done here. Logically, what bug did I just introduce by getting rid of the else and introducing line 10 on its own with no indentation outside of the conditional? What bug have I just introduced? What mistake to be clear? AUDIENCE: Name error. DAVID MALAN: Ironically, it's a name error but not a name error exception. It's an error with my name, but I think you're frozen for me. It's going to raise an exception because even though I'm checking the length of sys.argv up top and even though I'm checking it again for being greater than 2, not just less than 2, but greater, I'm still then blindly and incorrectly assuming it's now going to exist. So just to be clear, if I run Python of name.py and I don't type any argument-- I've got too few-- I think I'm going to see that I have too few, but I'm also going to see that same exception. At the very top of my terminal window's output, there's my error message, too few arguments. But again, on line 10, I blindly proceed to still index into my list at location1 which does not exist. So it turns out there's a better way to handle errors like this, especially if you're writing a program in Python that's just meant to run briefly and then exit anyway. But maybe we could start to exit prematurely if the program itself just can't proceed. If the user has not given us the data we want, perhaps, we should just exit the program earlier than we might otherwise. So let me go ahead and do this. Let me go ahead and remove my comments so as to focus only on the code here. And let me propose that instead of just printing, quote, unquote, "too" few arguments, I'm going to use one other function that comes with the sys module. I'm going to go ahead and call sys.exit. And as the name suggests, it's going to do exactly that. With the system's help, it's going to exit my program then and there on line four. Why is that OK? Well, if you gave me too few arguments, I have nothing more to say to you, the user. I might as well exit a bit prematurely. And I can do this as well on line six. Let's go ahead and not just print that, but sys.exit, quote, unquote, "too" many arguments. Print out that message and just exit right there. Now, I can trust that by the time I get to line eight, every error condition has been checked for. And so it's safe for me to assume that there is in fact an item at location1 in sys.argv. So let me go ahead now and run this, Python of name.py, Enter, too few arguments. But I'm back at my prompt. Nothing more has happened. Let me run it again. Python of name.py David Malan with no quotes, Enter. Too many arguments is now printed here. Finally, Python of name.py just David, Enter. Hello, my name is David. So we have then in sys two forms of functionality. Now, we have access to this variable, sys.argv, this argument vector, that gives me all of the words that were typed at the prompt, including the program's own file name. And it turns out if we read further in the documentation, there's an exit function that can take different types of input. But if I pass out a string like this, it will indeed print that string for me and then exit from my program then and there. Questions now on exiting from programs like this. To be clear, all of this time once Python gets to the bottom of your file, it's going to exit anyway. So I'm using sys.exit now just to make sure that I exit earlier than otherwise. AUDIENCE: My question is about the sys that arg-- argv. So is that capable of accepting or taking multiple elements at once? Let's say, for example, Python name.py, David Malan. I'm a male, 20 years old. And if let's say I only want to access your name, which is at the first index. And then your age is, say, at the sixth index. Can I say sys.argv1 and another one for six to access what I just want? Is that both for sys.argv? DAVID MALAN: Short answer-- yes, I think if I understand your question correctly, whereby, you're proposing to have many words at the end of the command. And you want to access those individual words. Absolutely. At some point, it gets a little fragile, I would say, if you're typing so many words at the prompt that the order really matters. And so it turns out there's a lot of programs. And there's functionality in Python that can allow you to provide those values, like name or age or any number of other fields in any order you want, but a pass in a bit more information textually that tells the program how you want to use it. So in short, what you're describing is possible. And let me do a small incarnation of it as follows. Let me propose that we go back to my code here. And let's propose that we actually now want to support multiple values at the prompt. So there's going to be no such thing as too many arguments. Suppose that I want to generate name tags not just for David, but for David, for Carter, for Rongshin, for others in the group who all want their name tags as well. So I'm going to go ahead and do this. I'm going to get rid of my elif condition because I don't want to limit the maximum number of words that are typed at the prompt anymore. I instead want to iterate over every name at the prompt. So I'm going to say this. For arginsys.argv, go ahead and print out this time, arg. So what am I doing here? Well, even though the syntax is a little different, the idea's the same as before when we've had loop. I'm using a for loop to iterate over a list. The list in question here is sys.argv. Arg is a variable that I'm creating on the fly. The for loop is going to make sure that the first time through this loop, arg is set to the first word on the command line. The second time through the loop, Python's going to make sure that arg is now set to the second thing on the command line and so forth. That's just how a for loop works. It updates the variable for us. I don't have to call it arg. I could call it name so long as I change it to name in both places. But arg is reasonable if I'm iterating over arguments more generally. If I now run this program, though, unfortunately, there's a little bit of a bug. Even if I type in David and Carter and Rongshin, I'm not going to get just three name tags. In your mind, does anyone see the bug I'm about to trip over? It's not a huge deal if I've got enough name tags to go around. But I'm going to be wasting one because this is going to print not three, but four name tags, whereby, the first contains the name of the program itself. Maybe not a big deal. Maybe that's the sticker we don't bother handing out, but it's wasteful. And it does look wrong. So how could we get access to not all four elements of argv but just a slice of argv? And this is actually a technical term in Python and some other languages. To take a slice of a list means to take a subset of it maybe from the beginning, maybe the middle, maybe the end. But a slice is a subset of a data structure like a list. Well, how do I actually do this in code? Well, in Python, it's actually very easy to take a slice of a list that is a subset thereof. You can simply do this. At the end of the list name, sys.argv in this case, you can use square brackets. And then in those square brackets, you can specify the start and the end of the list that you want to retain. I want to start at element1, not zero. I want to start at element1, and I want to just go to the end. So I'm actually going to omit a second number altogether. It's not necessary to have a second number. But I do need that colon because this is going to give me a slice of the list. It's going to give me a slice of the list that starts at location1, not zero. And the colon and then a blank just means it's going to give me everything else. So this is in equivalently going to slice off the first element of the list and give me a new list that contains just those three human names, not the name of the file itself. Let me try running this again. I'm going to run Python of name.py, David Carter Rongshin. This time hopefully, I'm going to get three and only three name tags, hitting Enter. And indeed, I've done now just this. So again, using some relatively simple syntax in Python, we can use square brackets not just to go to specific elements like bracket0 or bracket1. We can also get subsets of the list, slices of the list by doing bracket something colon something where each of those some things is a number, the beginning or the end, and they're optional depending on whether you want all of them or just some. Any questions now on this version, which adds the loop and these slices with that new syntax? AUDIENCE: Can we slice starting from the end of the argument-- argument vector? DAVID MALAN: You can. You can slice something from the end of the argument vector. And this might blow one's mind a little bit. Let me go ahead and do this. Let's see. Let me go ahead and do negative one at the end. Using a negative number here and running the same command, we've just uninvited Rongshin from receiving a name tag here. So if you use a negative number, it has the effect of counting in the other direction from the end of the list. A good question there. Other questions now on slices, on looping over sys.argv? AUDIENCE: Hi, so I remember very early on when we were talking about only having two decimal places in float value. Is that in the same vein, like, because we use the code on 0.2F? Is that the same thing then? Why would the F be included then in the 0.2F as opposed to here when you just have the numbers? DAVID MALAN: A really good question. And it's just the short answer's that context matters. So there's only so many keys on our keyboard. And so we sometimes use the same symbols for different things. So what you're alluding to is the format code in an F string for actually formatting a number using a colon, using a period, using a number, using the letter F and so forth. And that is very specific to the F string feature of Python. This case has nothing to do with any of that syntax per se. This is just using a colon in a different context to solve this problem to implement a slice. The authors of Python could have chosen another symbol. But honestly looking down at my keyboard here, we don't have that many to choose from that are easy to type. So sometimes they have different meanings. A good question as well. Allow me to propose now, that we take things further and move away from using only those modules, those libraries that Python comes with to talk about more generally packages that exist. One of the reasons that Python is so popular and powerful these days is that there's a lot of third-party libraries out there as well, otherwise known as packages. Strictly speaking, Python itself has a term of art called a package, which is a module essentially that's implemented in a folder, not just a file but a folder. But more generally, a package is a third-party library that you, that I can install on our own Mac or PC or our cloud server and gain access to even more functionality that other people have implemented for us. Now, one of the locations you can get all of these packages is called the PYTI website, the Python Package Index which lives at this URL here. And this is a website that is searchable via the command line, as well as via the web, that allows you to download and install all sorts of packages. Even CS50 has some of its own packages in services like these. Now, there's a fun one out there that's a throwback to a command that's been around for years in command line environments called cowsay. Cowsay is a package in Python that allows you to have a cow say something on your screen. If curious to read up on it, its own documentation is on pi.py.org specifically at this URL here. But how do you actually get the package into your system? Well, technically, you could figure out how to download the file and maybe unzip it and put it into the right location on your Mac or PC. But nowadays, a lot of languages, Python among them, has what's called its own package manager. This one here called pip which is just one. So pip is a program that generally comes with Python itself, nowadays, that allows you to install packages onto your own Macs or PCs or cloud environment by just running a command. And then voila, you have access to a whole new library in Python that didn't come with Python itself. But now it's available on your system for you. Let's go back to VS Code here. And in my terminal window, I'm going to go ahead and type pip install cowsay. Now, what's going on here? Pip is the command, the package manager. And I want to install what package? The package called cowsay. I'm going to go ahead and hit Enter here. And after a little bit of output, it has successfully installed cowsay. Now, what does that mean? That means I can now go about importing this into my own code. Well, let's go ahead and see what this means. So let me go ahead and create a new file with code called say.py because I want something to be said on the screen. And in my new tab here, I'm going to go ahead and import cowsay, which presumably is now installed. I'm now going to import sys as well because I'd like to use some command line arguments in this program just so that I can run it quickly. And without using the input function, I can get the user's name immediately from the prompt. And let me go ahead and do this. I'm going to do a bit of error checking proactively this time. And rather than use less than or greater than, I'm this time going to say if the length of sys.argv does equal 2. So if the human is provided just the name of the program and their own first name, we're good to go. I'm going to do the following. I'm going to call a function called COW in the package called cowsay. And I'm going to pass in a string, hello, comma. And then as in the past, I'm going to pass in just one string because according to its documentation, it's not like print. I can't pass in comma this, comma that. I can only pass in one string. So I'm going to concatenate it the contents of sys.argv, bracket1. So long as then I type in my name David after the name of this program, it should end up in sys.argv1 in which case, this line five of code should concatenate hello with my name with a space in between. And apparently, a cow is going to say it. So let's see what happens here. Let me go ahead and clear my screen and increase the size of my terminal window. Let me go ahead and run Python of say.py and type my name David and Enter. There is the program called cowsay. It literally has a cow say something on the screen. And this is a throwback to a program from yesteryear that tended to come with a lot of systems. This is otherwise known as ASCII art. It's a textual way using just keys on your keyboard to print pictures of sorts on the screen. Now, we can really go down the rabbit hole here. And there's questionable academic value of doing so. So I'll do so just once. Turns out the cowsay package comes with other functions as well. One of those functions, for instance, is T-Rex. And if I now increase the size of my terminal window, we'll perhaps see where we're going with this. Let me now run again, Python of say.py. This time, let me not provide my name just to see if it's broken. It's still OK because we have that if condition if the length of sys.argv equals equals 2 and only if it equals equals 2, do we do anything. That's why we're not seeing anything here. Let me go ahead and cooperate now, say.py space David. And it's no longer a cow. But if I zoom out on my screen, a T-Rex. Why? Just because these are the things you can do once you know how to program. You can even package them up and make them freely available to others as open source software. For us, it's demonstrative of a feature more generally here namely being able to install these third-party packages and how you might do so in Python. Now, I'll leave this up on the screen for a moment and see if there's any questions about cows or Tyrannosaurus rex's or packages more generally. I'm really qualified to speak to just one of those. AUDIENCE: Hi, I've got two questions it's a bit earlier than what's supposed to be. So the first question is the packages that you're calling to use in the program, are they the same as, let's say, something Java the same as calling a class, a Java file in order to use its functions? And my second question is, what's the actual purpose of using command line arguments as you used because is not really the best way to, as you say, be user friendly where as in let's say the person who's using the program doesn't know what they want-- what the program's asking them? DAVID MALAN: Really good question. The first question about the comparison with Java, Python packages are similar to Java packages where you have something.something.something at the top of your program that gives you access to a class or something else. Python itself supports classes. More on those down the road. And you can do very similar things in Python as you can do with Java. But the analog really is Python packages to Java packages here. As for command line arguments, you ask a good question. Why do we use them, especially if they are literally user friendly? They're a little less user friendly to people who aren't in this Zoom to be honest. You and I as we learn more and more about programming and more about command line arguments, I daresay we'll become more comfortable with and tend to prefer the ability to customize commands using these command line arguments. Why? Productivity. It tends to make you faster because you get into the habit of knowing exactly how you can configure your software without having to manually answer questions. And case in point. All of this time have we been running Python of something.py. You could imagine not doing that. You can imagine typing only Python, hitting Enter. And then you're prompted for the name of the file you want to run. So you type in something.py, and then it runs. Not a big deal, but I would argue that over time, you're going to get a little tired of that TDM. And you would much prefer to just automate the command again and again and again, especially with little conveniences like being able to hit up and down in your keyboard history so as to rerun those same command. Automation is big too if you emerge from a class like this and start using Python to automate processes at work or for personal projects or the like, the ability to specify all of your inputs on the one line just means you can get work done more quickly. So hands down, absolutely. Using command line arguments is a more arcane feature of systems that most of us are no longer as familiar with because of Windows and Mac OS and other operating systems that have buttons and GUIs and menus. But the more comfortable you get with programming, I daresay the more you will tend to prefer these capabilities because they allow you to do things more quickly. With that said, allow me to propose that we take a turn toward, yet, another package that's particularly popular and just as easy to install all toward an end of using APIs. Now, APIs are not something that's Python-specific. More generally, an API is an application programming interface. And it can refer to Python files and functions. But often, APIs really refer to third-party services that you and I can write code that talk to. Many APIs, but not all, live on the internet these days so that so long as you have a browser or so long as you have some experience with Python programming or programming in any language, you can write code that in effect pretends to be a browser, connects to that third-party API on a server, and download some data that you can then incorporate into your own program. Now, how do you do this? Well, Python has a very popular package that you can install via pip called requests. The requests library allows you to make web request, internet request using Python code essentially as though you were a browser yourself. You can automate, therefore, the retrieval of URLs that start with HTTP or HTTPS. The documentation for this library is that a URL like this, but it too can be installed at the command line. And even though it's third party, it's one of the most popular and commonly used packages out there in Python. And this too is one of the reasons again that Python is so popular. There's just so many solutions to problems that you and I have or are invariably going to have when we write projects of our own. There's just a really vibrant ecosystem, a really vibrant community of open source software that's that easy for us to install. Let me go back to my terminal window now and run pip install requests in order to install this package on my own system. And after some lines of output, I'll see that it's successfully installed. Now, let's go ahead and create a new file here. For instance, itunes.py. It turns out that Apple has its own API for their iTunes service. The software that provides you with the ability to download and search for music and songs and other information as well. And it turns out that-- let me go back over to my computer here and open up a browser like Chrome. And let me go ahead and visit this URL here, https://itunes.apple.com/ search?entity=song&limit=1&term=weezer. Search?entity=song& limit=1&term=weezer. Now, I constructed this URL manually by reading the documentation for Apple's API-- application programming interface for iTune. And what they told me is that if I want to search for information about songs in their database, I should specify entity equals song so that songs and not albums or artists or something like that. If I just want to get back information on one song, I'm going to provide limit equals 1. And if the band I want to search for, the artist is Weezer, I should specify term equals Weezer. So with this, if I go ahead and hit Enter and visit this URL, I actually end up with a text file in my Downloads folder on my Mac. If I go ahead and open that text file that my browser just downloaded, we'll see all of this text here, which at first glance might look a bit cryptic, but it actually follows a pattern. Notice this curly brace at the start and notice this closed curly brace at the end. Notice this open square bracket here and notice this closed square bracket here. And in between those pieces of syntax are a whole bunch of strings and values. In fact, a whole bunch of key value pairs. What we're looking at here is a standard text format known as JSON-- JavaScript Object Notation, which yes, is technically related to yet, another programming language called JavaScript. But JSON itself is typically used nowadays as a language agnostic format for exchanging data between computers. By language agnostic, I mean you don't have to use JavaScript. You can use Python or any other language to read JSON or write it as well. And it's a completely text-based format, which means that if I visit that URL with my browser, what gets downloaded is just a bunch of text. But that text is formatted in a standard way using curly braces and square bracket using quotes and some colons that ultimately contains all of the information in Apple's database on Weezer's song, at least, the first one because I limited it to one in their database. And that's an API, an application programming interface. A mechanism whereby I can access data on someone else's server and somehow integrate it into my own program. Now, of course, my browser, Chrome, is not something I wrote. I should actually write some Python code that perhaps pretends to be a browser to grab this same data. So let's do that. Let me go back to VS Code here. And let me write a program with code, itunes.py. And we're going to write some code via which I can then use the iTunes API and in turn, Python to get information about any band that I might want. I'm going to go here and import first the requests library, which I installed earlier in order to make those HTTP requests. I'm going to go ahead and import the sys library via which I'll have the ability to use command line arguments like specification of the band that I want to search for if not Weezer. And then down here, I'm going to go ahead and insert some error checking to say if the length of sys.argv does not equal to-- so if the user does not provide me with the name of the file they want to run and the name of a band, and that's it, you know what. Let's just go ahead and exit for now. I could provide a more explanatory message. But for now, I'm going to keep things simple and just exit the program prematurely so that I can trust hereafter that sys.argv has what I want. And now, I have the opportunity to use the requests library to write some Python code that effectively is pretending to be a web browser so as to connect to that same HTTPS URL on Apple's own server. So now that I've guaranteed that the user has typed in not just the name of the file, but also the name of a band at the prompt giving me a length of two for sys.argv, let's go ahead and execute request stockget, which is a function inside of the request package that will literally get some response from a server. And the URL that I want to get is the exactly the same as before. https://itunes.apple.com/ search?entity=song& limit=1& term=previouslyweezer. But let's make this program a little interactive and actually allow the human to specify at the command line what artists they'd like to search for. So I'm going to go ahead and close my quote early and just append using the concatenation operator as in the past, sys.argv bracket1. And now, it actually be nice to store the response from the server in a variable. So I'm going to go ahead and say response equals and to store all of the response that comes back from the server in a variable called response. Down here now, I'd like to just understand what the server's returning to me to make sure I know how next to proceed. So this isn't going to be very pretty yet. But I'm going to go and print out response.json, which ensures that the data I'm getting back is formatted on my screen as exactly that, JSON, the same text format as we saw on my screen. It's not a useful program yet. I'm really just learning along the way. But let me go ahead now and increase the size of my terminal window and run Python of itunes.py and type in the name of a band like Weezer and hit Enter. And what we see on the screen formatted almost the same as before is exactly that same text. But what you'll see here is that this has been standardized now as a Python dictionary. What indeed Apple's returning is technically a JSON response, JavaScript Object Notation. But Python, the request library is converting it to a Python dictionary which happens to use wonderfully coincidentally, almost the same syntax. It uses curly braces to represent the dictionary here and a close curly brace to represent the end of it here. For any lists therein, it uses a square bracket here and a closed square bracket down here. It uses quotes-- single quotes in this case or equivalently double quotes to represent the keys in that dictionary. And after a colon, it stores the value of that key. And so you'll see that indeed we have a result count key whose value is 1, but then a more interesting Result key called results whose value is this entire list of data. Now, honestly, this is such a big blob of text that it's going to take me forever to wrap my mind around what I'm seeing. So let me propose temporarily we use another library in Python that will allow me to format my data a little more cleanly. It turns out that Python also comes with a special library called JSON that allows you to manipulate JSON data and even just printy print it that is formatted in a way that's going to be way easier for you and I to understand. So let me go back to my code here. Let me shrink my terminal window. And let me propose that just temporarily again we do this. Let me import this additional library, JSON, which comes with Python. So I don't need to install it manually with pip. And let me go ahead now and not just print out response.json which was that big blob of hard-to-understand text. Let me go ahead and use one other function here called json.dumps for dump string and pass to that function that response.json return value. So again, I'm just introducing another function who I claim has a purpose in life of pretty printing, nicely formatting on the screen the exact same information. And I know this from the documentation having done this before. But I'd like things to be nicely indented. And according to the documentation, if I pass in a named parameter of indent equals 2, that's going to indent everything at least two spaces. I could do four or something else. But it's going to be enough to help me wrap my mind around what the data is I'm getting back. Because again, I'm just learning along with you. So let me increase the size of my terminal window again. Let me run Python of itunes.py. And again, let's search for Weezer and hit Enter. And now, notice it's still a little bit cryptic because there's a lot going on here. But my gosh, I can totally read this more easily now. Notice now that I still see the first curly brace, which means hey, this is a dictionary in Python. A collection of keys and values. The first key is called result count. It happens to be displayed in double quotes now. But that's just an issue of formatting. It could be double or single so long as we're consistent. The value of that key is one. Why? Well, I told the URL to only limit the responses to one Weezer song so I've gotten a result set of one. If I increase that limit, I could probably get more. Then the interesting part of this response is really the data itself. Notice in the results key here, there's a really big value. The value is a Python list as implied by this square bracket. What does this list contain? Well, I know from skimming it earlier that this contains one dictionary. And that's why we see another curly brace here. So again, if this gets a little more complicated, keep in mind that a dictionary is just a collection of key value pairs. And Python uses curly braces to indicate as much. It is perfectly reasonable for a dictionary to be inside of another dictionary if the value of some key itself is another dictionary. So this is a common paradigm. And even though it might seem a bit cryptic, it's just something that allows us to associate more keys with more value. Now, most of this information, I probably don't care about. For instance, according to Apple, the unique identifier for Weezer is apparently 115,234. That might be useful if I'm making my own database and I want this to be searchable. But for today's purposes, all I care about is the name of the track, otherwise called track name as key. And the first song and only song because we limited it to one that we got back from iTunes here is the song that you might know by Weezer called Say It Ain't So. So now, I have a bit of a clue if my goal here is to implement a program called itunes.py that doesn't just dump the response from the server, which is admittedly very cryptic-- but to print out all of the songs that iTunes has for the band called Weezer, maybe I can iterate over this somehow. So let me backtrack. Here's the key call track name. It is inside of a dictionary that is the value of results here. So how can I go about getting this? Well, let me go ahead and try this. Let me go ahead and shrink my terminal window back down and let me propose now for one final flourish. We don't just lazily print out the contents of that response because that's not interesting or pretty for anyone. Let's do this. Let me go ahead and create a new variable just for the sake of discussion called O for object. And I'm going to go ahead and call o equals response.json just to store that JSON response specifically in a variable called o, but I could name it anything I want. And now, I'm going to do this. For each result in that object's key called results, go ahead and print out that result's track name. And notice I have used exactly the same capitalization. Track name has a capital N. Result is all lowercase. And let me rewind before we run the actual program. In line eight, we are making an HTTP request using Python to the server just like you and I as humans type URLs into a browser and hit Enter. This is the Python equivalent thereof. I am then on line 10 just grabbing from that variable that contains the server's response, the JSON object that I care about. The thing between those curly braces at the very top and the bottom. But because we've poked around and because I read the documentation earlier, I know that that object has a key called results. And that results key again is a list. Now, at the moment, that list contains only one song, Say It Ain't So, because I limited my response to one. But even so, my loop will work. It's just going to iterate once. And each time through that loop, it's going to print the current result's track name. If I want to make this even more interesting, let me change this limit now from 1 to 50 so I'll at least get back 50 track names instead. Let me go ahead now and increase the size of my terminal once more and go ahead now and run Python of itunes.py searching again for a band like Weezer. And here we go. And voila, there are 50 songs that iTunes has for Weezer. And if we scroll back up to the top here, we'll see that the very first one there is, indeed, Say It Ain't So. But now, we got Undone-- The Sweater Song, Buddy Holly. Apparently another rendition of Say It Ain't So perhaps from another album, another Buddy Holly undone, my name is Jonas, and so forth. Questions now on this program which integrates Python with a real world third party API? AUDIENCE: Yeah, hi. Can we use break instead of system.exit? DAVID MALAN: Good question, but no. Break again is used to break out of things like loops like we saw earlier. Sys.exit is used to break out of the whole program itself. Use break for loops for now and use sys.exit to terminate the whole program. Good question. Other questions now on this program are others? AUDIENCE: From where we bring the name of the key results? DAVID MALAN: From where do we get the name of the key? AUDIENCE: Results itself. Yeah, and can we change the results, the name? DAVID MALAN: You cannot. So we could in our program. So the keys that come back in that JSON response, to be clear, come from iTunes.Apple.com. Some team of engineers decided for us what all of those keys would be called, including track name, results, result count, and everything else. You and I can absolutely store those same values in variables just like I'm doing here with O, just like I'm doing here with result. You can rename those keys anything you want using Python variables. But the JSON response is coming from that third party server. Other questions. AUDIENCE: Yes, sir. So I have a question related to cowsay package. So like, yes. So sir, what sort of ASCII graphics is it capable of putting? DAVID MALAN: The cowsay package. I would refer you to the URL in the slides earlier if only because it's more thorough. They have not just cows, but Tyrannosaurus rex's and several other animals as well. I should emphasize that this is not a package I suspect you will use much in the real world. It's really just meant to be representative of the types of packages you can install. But allow me to refer to the documentation for what more is there. But ASCII art is all we had before there were emojis, let alone GIFs and JPEGs and pings. But it's what's is immortalized in cowsay. Well, allow me to transition us back now to one final capability of Python which is that you yourselves have the ability to make your own libraries. Up until now, we've been writing all of our functions in our one file, Hello py and everything since. And now that we've introduced modules in Python, random and statistics, we can import those that come with Python. But that's other people's code as well. And we've now used pip, this package manager to install third-party packages as well in the system and using other people's code still. But to come full circle, what if you yourself find yourself implementing the same kinds of functions again and again, or you find yourself opening up old programs, copying and pasting code you wrote into new programs because you have the same problem yet again? A good practice would be to somehow bundle up that code you keep reusing and make your own Python module or package. You can keep it local on your own Mac or PC or cloud server. Or you can go through the steps of actually bundling it up, making it free and open source, and putting it on something like py, pi for others to use as well? OK, I'm going to go ahead and run code of sayings.py to create a brand new file called sayings.py which is going to be my own sayings module. I'm going to define a couple of simple functions in there. I'm going to define a Hello function that's going to take a name parameter as input. And that function is simply going to print out an F string that contains hello, comma, and then in curly braces, whatever that person's name actually is. Then I'm going to go ahead and define one other function, a goodbye function, that has def, goodbye, also takes a name as its input. And then that prints out by contrast and F string that says goodbye, comma, and then in curly braces, name. And now, just for good measure just so I can be sure that these functions are working as expected, I'm going to go ahead and define a main function in here too just for the purposes of testing. And I'm going to go ahead and define a main function that simply does a couple of tests. For instance, it calls Hello of, quote, unquote, "world" shall we say? And then it's going to call good bye of, quote, unquote, "world" as well. And hopefully what I'll see on the screen then is Hello world and Goodbye world when I run this program. Of course, as always, I need to explicitly tell Python to call that function. So I'm going to call main at the very bottom of this file. All right, let's try it out. Python of sayings.py, Enter. And indeed, I see Hello world and Goodbye world. And so I think it's reasonable for me to assume that these functions, albeit simple, are pretty correct at this point. But now, suppose that I want to use these functions as though I've indeed created my own library, my own Python module and that makes available a Hello function for me or anyone else who wants to use it or a Goodbye function as well. Well, let me go ahead and open up again say.py but start fresh. And rather than have the cow say anything, let me go ahead and have my own library do the talking. So I'm going to go ahead and as before, import sys so that I have access to command line arguments. And from my own module called sayings, I'm going to import Hello. So because I created a file called sayings.py, I can say from sayings. And it's inferred by Python that I mean sayings.py, at least in this current directory. But I specifically ain't going to import just one of the functions for now, namely Hello. And now, I can do something like this. If the user obliges by giving me two command line arguments, which I can check by just checking the length of sys.argv, I'm going to then go ahead and call this new Hello function passing as its input, sys.argvbracket1, which should hopefully be the person's name which I'm going to expect them to type at the prompt. So here we go. I'm going to go down to my terminal window, run Python of say.py and my own name because I want my own name to end up in the command line arguments and therefore, be part of the Hello so when I hit Enter in just a moment, I should hopefully see Hello, comma, David. So here we go, Enter. And huh, I see Hello world, Goodbye world. And then I see Hello, David. So why is this happening? Well, it turns out even though I've done everything according to our own past practice, it's not really the right way to go about calling main after all. If I'm blindly calling main here at the bottom of my file, that means whenever this file is loaded by Python, main is going to get called. And unfortunately, that's true even if I'm importing this file or just a function from this file as I am here in my say.py program. This is to say on line three here, when I say from sayings, import Hello, this effectively tells Python to go find that module, sayings.py, read it from top to bottom, left to right, and then import specifically the Hello function. Unfortunately by the time Python has read the file from top to bottom, left to right, that last line of code recall is to call main. Main gets called no matter what. So really, the right way to go about using a main function, which does solve that problem of ensuring that we can order our functions however we want, and all the functions will be defined at the time they're invoked, I shouldn't be unconditionally calling main at the bottom of this or really any of my programs. I should instead use this technique. I should say if__name__=="__main__", then and only then should you actually call main. Well, it turns out that this variable is a special symbol in Python, __name__. And notice that VS Code because of its font isn't quite showing those two underscores. But they're indeed there to the left and the right. This is a special variable whose value is automatically set by Python to be, quote, unquote, "main" when you run a file from the command line as by running Python of sayings.py. So watch what happens now with this additional conditional in sayings.py. If I run Python of sayings.py, it still works as before because name will be automatically set to __main__ when I run this file using Python of sayings.py. But notice this. Name is not going to be set to, quote, unquote, "main." It's going to be set to something else, technically the name of the module when I instead import the file like I do here. So this highlighted line of code even though it will cause Python to go find sayings.py, read it from top to bottom, left to right, it's going to ignore the call to main this time because it's wrapped in that conditional. In this case, when I'm importing a file and not running it directly at the command line, main will not get called by definition of that names value. So let me go ahead and try this. Instead of running Python of sayings.py, which is the module, which contains that conditional main, let me go ahead here, run Python of say.py, which is the program here before me that imports Hello from sayings. But because of that conditional, it's not going to say Hello to anyone else except me in this case. All right, we're here at the end of our week. It's only appropriate, I think, to import something other than Hello. Why don't I go ahead and import not Hello, but Goodbye from here. Let me go ahead and call Goodbye instead of Hello. And this time when I run Python of say.py, I'm not going to type my own name. Allow me if I may to type in the whole world so that our final sentiment today is Goodbye world. Indeed, that's it for this week. We will see you next time.