Transcripts
1. Welcome to Python 301!: Hello and welcome to Python 301. I'm Caleb Colleen and I'm going to be your instructor today. In this course, we are going to be learning about some more advanced Python concepts, such as object-oriented programming, using classes, try except generators, decorators. And then at the end we're going to create a banking app to get the most out of this course, you should already be somewhat familiar with Python. You should consider yourself an intermediate level or a junior level, a Python programmer. And you're going to be basically doing a little bit of homework at the end of every single lesson, we're going to be learning all about classes, properties, methods, defender methods, class inheritance, class interfaces, and all sorts of things in between. Then we're going to be gracefully handling basically errors so that our program doesn't just stop working. So that our program can gracefully handle an error and keep on working. And as I mentioned before, at the very end, we're going to have a project where we create an app, a banking app where you can withdraw or deposit money using Python. And then you're going to log all of your transactions into a transaction file. I'm Caleb Colleen and I've been working with Python for a number of years now. I use Python every single day, both professionally and for Hobbes. And today I'm going to be teaching you advanced Python. Welcome to Python 301 with your host and instructor, Caleb Tallinn.
2. Creating your first OOP class: Let's get started with reading our first Python class. So first of all, you need Python installed and you need to be able to write a little bit of Python to make use of this module, to make use of Python three, oh one. So in front of me here I just have VS code is just a simple text editor. You can use PyCharm, you can use brackets, you can use any text editor you like. I just happened to like VS code. So in front of me here, what I'm going to do is go to View and then go to terminal and just open up that terminal. And that's just going to give me a terminal at the bottom and my file viewer on the left. Let's go ahead and create a new file here. And let's call this first class dot py. So let's take a look at writing our first-class. But before we do that, we need to take a look at some of the syntax. So when we're writing a function, it's like something along the lines of d f, function name. Do a thing. A class is a little bit different. Now to instantiate a function, we simply say the function name, and that's it. Now a class is a little bit different. A class starts with the word class, and this is going to create what's known as an object. And in Python, everything is an object of file as an object. A function is an object. A class isn't object, but they're managed slightly differently. So a class starts with the keyword class and then we typically give it upper case naming, or at least capitalized words are capitalized letters. So we say something like class. This is an animal. And that's literally all there is to creating a class and then to instantiate that class or, or to use less fancier terminology to activate this class, we say animal is equal to, this is an animal. At this point, this class is completely useless, but this will work. This won't give us any errors. And we can demonstrate that by going into our terminal and typing Python first-class dot py. And we don't see any errors. That means this is working the way we want it to work. If you don't see errors, that's a good thing. So let's go ahead and create a real life example here. So this example is going to be literally just an animal example. And we're going to use this example through pretty much all of this python 301 module or this course. So let's go ahead and create a brand new class. We say class. We give it a name colon, and then we can do something in here. Now in the next few lessons, we are going to talk about properties and methods and things like that. But for now, let's go ahead and give this a symbol property. And this is really just a fancy word for a variable inside of a class. And so we have something like a property. And this is actually keywords were not going to use that will use like property one is equal to something. Then we can instantiate this class or activate this class by saying the animal is equal to an animal class. It has the parentheses. There are a lot like a function. And then we can say print the animal dot property one and you can see it audiophiles for me using VS Code. And so this is a lot like a dictionary object, but a dictionary looks something like this. It's looking up a keyword. In a class we use dot notation and that dot simply means this is going to be some sort of class and this is some sort of property or a variable inside of that class. Let's go ahead and run this. And we see that it says something, whatever this isn't here. Now we can overwrite that and we're going to talk about that in future lessons. We can completely change that. We don't have to use property one at all. We can literally name this. Anything else? I'm going to select this. Go to view or is its selection, selection add next occurrence. And it's going to select both of these. So I can write in two places at once. And I can say, this is a property. And when I run this again, it's simply going to print out the word something. And so you can see that these two, when I, even when I click it there, highlighted at the same time, these are married. And so to make sense of this, we created a class called animal, gave it a property, just a boring, bland property. We hardcoded it, so it's actually not a lot of use right now. There's not a lot of use for it. Then we instantiated that class and we throw it in a variable. And by doing that, we activated this class or instantiated this class. Then we said in that variable activate the property. This is a property. Now, right now this is a lot like a dictionary. The syntax is slightly different, but this is a lot like a dictionary. So what I would like you to do is create an example Animal class or really any sort of class. Give it a property, hard code the value, activate that class, and then print out that property. So essentially do what I did in this video. When you're done that, let's head on over to the next lesson where we talk about class properties a little more in depth.
3. OOP Class properties: Welcome back. In this lesson we are going to be talking about class properties. So to carry on from our last lesson, I'm simply going to copy and paste what we have from our previous lesson, which I just renamed from the last video. Just so it's a little easier to manage these files when you download the source. But I'm going to basically take this exact code and move it into this next lesson, it, and I'm going to do this moving forward through Python 301. So in the last lesson, we talked about this being a property. We have a class, this is a property and this is hard-coded to be the string, something. Now this doesn't have to be a string. This could be a Boolean, a list, or a dictionary, or it could be literally anything. But let's make use of something that's a little more complicated. So let's make use of a dictionary. So we're going to have a property in here. It's going to be set to some form of a dictionary. And in here we could say T1 is then equal to value one. And then we have to instantiate that. We did this in the last lesson. We use the variable after the animal class has been instantiated. This is a property and this is going to print out a dictionary. So if I run this, it prints out a dictionary. Now we know that this is a dictionary, and at this point this is just a standard variable. There's nothing special about this. It just happens to be that there's a class here. Then a property. Consider it a variable. And then to access a key like we would in a regular dictionary, we just give it the key name and that matches right here. So when I save this and rerun the script, we can see it says value one. And so we're going to see this a lot in professional level Python, we're going to see property set as dictionaries. This is a very common thing. And so it's going to be really useful for you to know that this is a class, this is a property. And this property, its datatype or its structure, is a dictionary. Which means we can then look things up, but it doesn't have to necessarily be a dictionary. We could say this list and I'm just gonna give it any name is equal to a list of maybe names. So we've got cane, Caleb, and gully, who is a cute little cat, actually is not literally is a pretty big cat. And then we could say, print the animal, this list. And when we re-run this, we're going to see down here, it says Cain, Caleb, and gully, and it shows up as a list. Now because this is a list we can do indexing and slicing and stuff on it just like a regular list variable just happens to be it's inside of a class. That's really all there is to it. So if we wanted to get, let's say gully, remember lists start at 0. So this is 0 index one, index, two index. We'd say this list. Hard brackets to, and this is going to print out the word gulley or the name gully for us, just like that. Now when it comes to automatically assigning properties, we can do that with this thing called a defender method, a double underscore method. We're going to talk about that in its own particular lesson. We can also use functions as properties, and that gets to be a little more advanced. We're going to talk about that in the next lesson. So in this lesson, what I would like you to do is create a class, give it to properties. And instead of just using a string or a number or some sort of simple datatype. Use a new type of data type are not a new one, but a more complex data structure. So the first one should be a dictionary and the second one should be a list. And then try to access that dictionary enlist from within the class. Now a fun thing we can do because programming languages we have these things called private properties. Python doesn't have private or public. It's all just sort of accessible. So what we could also do is print animal dot this list. And that's just simply going to, instead of throwing the class into a variable, we're going to access this property by its class directly. So if we go ahead and run this, we're going to see prints out our list for us as we expect. So there is no private, there is no public, Nothing like that. I know PHP and Java and all these other languages have that. Python doesn't have that. Oh, and one more thing, actually, I should not forget this. You're going to see this all the time. Whenever you see a property, start with an underscore like this. This is a private property. So this is how we get away with it in Python, instead of saying this is going to be private, like in some languages, we literally use the word private. We start with an underscore. And when we start with that underscore, we're simply saying this is a private property. Don't access it from outside of the class, so don't access it this way. It should only ever be used internally. Now we don't know how to use those internally yet. So we're going to learn about that in the next couple of lessons. But what I would like you to do is go ahead and give this a shot, try this out. Create an animal class dictionary, list, private property, and then instantiate your class. Print something out from your dictionary or lists, and then try to print just straight from that animal, that animal class. One of the properties without instantiating it and without throwing it straight into a variable. So there's two ways to do this. Go ahead and give that a shot. Try it out. Please. Try it out. This is important because when you get into the world of, for example, machine learning or even just Django web framework. You're going to see classes everywhere. It's important to know how classes work in Python. Thankfully, they're pretty easy to learn. So in the next lesson, let's go ahead and talk about class methods.
4. OOP Class methods: Hello, hello, hello. In this lesson, let's go ahead and talk about class methods. Class methods. Honestly, it's just a fancy term for a function inside of a class. And so it looks exactly like a function. There's only one distinct difference here. So let's go ahead and get rid of this private variable that's just from the last lesson. And let's create a new method. So this is a method. This is all there is and this looks exactly like a standard function. And so will all a method is, is a function inside of class. Now one thing to note here is that on a standard method inside of a class, it's first parameter is always self. Now if you come from like a JavaScript background, you're going to see this, or PHP uses this as well. In PHP uses something like this. I then gets dollar sign this and then an arrow. Javascript uses the this keyword. In Python, we use the self keyword. And what that does is when we say self, we can now print self dot and we have access to everything. And you can see VS code is automatically filled. This for me, we have self dot. This is a method, this is a property, this list. So let's go ahead and print this list. We've written a method here. Now let's go ahead and activate it. And to activate it or to run it. We're going to instantiate this animal class because this is a best practice. Instead of accessing animal on its own, we throw it into a variable. Then we say the anime owl dot. This is method parentheses. And unlike a function where we see a keyword argument or a required positional argument in here like self, we don't have to do that, we can simply ignore it. So even though self is used inside of the class, outside of the class, we completely 100% ignore it as if it doesn't exist. And that's just saying because this is a class object inside of here, it's going to have access to everything inside of the class. And the reason we do that is so that we don't have to constantly put in new arguments. So we wouldn't have to say this list. This is property, et cetera, et cetera, et cetera. We don't have to constantly pass these things in because we would have to put in a list here. And then this next one is going to be a dictionary there already set in here. We don't have to pass those in, so I'm going to undo that and simply run, this is a method. Print self, this list. And again, self is referring to animal. This list property, class, property. Let's go ahead and run this Python 301 lesson three class methods, and it prints it out for us. So with the self keyword, we have access to other methods, other properties, all that other good stuff inside of this class. We don't have access to other classes yet. It's only this particular class and that's what self is referring to. Self is referring to the animal class that we are working in. Next up, we can also define properties using methods. And again, to boil this down into more layman's terms, we can create a class variable based on a class function. Let's go ahead and create a function or a method called get. Golly. It's going to take self as its first parameter, always does. And we could return because it's basically a function, self.age, this list. And then gully is number two, its index to 012. Now, when we try to get this, we're going to have to say, get golly with parentheses. Now if this is supposed to act like a property instead of a function, let's say there's nothing in here and it just supposed to return a simple piece of data. We can use a thing called a decorator, which we'll talk about in the future as well. And we just say at property. And what this is going to do is say, any sort of logic in here, any logic and this method could get huge, though it could be a 100 lines of code in here if we wanted to. It's always going to put this into a property. And when this is a property, we ditch the parentheses. Now because we're using the return keyword just like a function, This isn't actually going to do anything. This is a method up here, is actually printing. In this case, let's make this a little more complex. Let's use a method like a regular function and actually returned something. So then we can say gully is equal to the animal, get gully. And that's going to allow us to use this later so we can print. The cutest got toe of all time is Golly. Let's go ahead and run this. And we're going to see that this prints to different things. It's going to print. This is a method because it's executing this which is going to print. And then we're going to print the cutest Gatto if all time is galley. And look at that. It prints our list just like it did in the first example, in the second example and our second print statement. This is our class, This is our property. And you can see it actually looks a lot like a function, but it's got a decorator on it, basically saying, hey, even though that this is an executable object, or what we call a callable. Treat it as if it's not a callable, treated as if it's a standard property like this list. And this is a property. And because we use the return keyword, is going to be able to then throw that into a variable, any variable name. And then we can use that variable down the road. Now let's take a look at one more example. Let's, let's create an example where we add a name to this list. So in here we can say death, because we're going to create a method. Add name. It's always going to take self and the name is going to be the parameter that we're going to add. Then we can write self dot, this list dot append that name. And we can, if we wanted to return either self.age, this list as the entire new list because all we did was take a list. If you forget about the self for just a second, it looks just like a regular list, list.append. This just happens to be accessing it from outside of the function or the method. So we have class property. It's a list so we can use dot append on it. We're going to append the name here. And then it's going to return this list for us. And we're going to see this a lot. So let's go ahead and comment out that code. And let's say the anti-malware dot. And what did we call it? We called it add Name and it takes a name. Ignore self. Self always goes in there but we ignore it. Add Name, and then the name is going to be rhubarb. Then if we wanted to, we could print the animal dot this list. And we're going to see that this list now has Doo-doo-doo-doo came Caleb gully, and rhubarb. And so we're dynamically adding things to our properties now using a method. Go ahead and run this. And it says Cain, Caleb gully, and rhubarb just as expected. And again, all we're doing here is we're using datatypes, standard Python datatypes inside of a class. And so we're encapsulating or, you know, to boil that down because we like our big words as developers, incentive encapsulate, Think of it as contain. So all we're doing is containing all of this code in a class. And the idea here is that we're grouping functions and variables or methods and properties. We're grouping them together in this thing called an animal, this class called an animal. And the idea here is that all of these are related somehow. So what I would like you to do as your task for this lesson is create a class animal. Create a property, make it an array, or make it a list, a Python list. Then write a method to not add something but to delete something from that list. So instead of adding, it could be called remove name. Remember take self as its first parameter a 100% of the time it always takes off. Inside of this, a particular method, we can then access self dot, whatever your list is called, dot remove and then the name. And then down here we can say, well, we instantiated it. This is our class, This is our method name. So yours would be called remove name, and then the name to remove. And then you can print out that entire list again and see that it was actually in fact removed. In the next lesson, let's go ahead and clean this up. So this looks a little more leg, a real-world example because right now this is messy code and this is just tutorial code. This is no good. Let's create a real example of an animal class in the next lesson.
5. OOP Class cleanup: I write, taking off from that last lesson, what we're going to do is clean this up a little bit and create somewhat of a real animal instance, or sometimes we call the class instance because it is an instance of a class. And we're going to basically get rid of all of this and we're going to create a proper animal class. So what kind of things doesn't animal have? An animal has a fur color. And let's say that for a color is going to be orange, then an animal also has actions it can take. What kind of action cannot take it could def, eat, it could do something. And remember, we always put self in here. We could also do deaf Chase because it's going to chase down its food. Let's say this is like a tiger or a cheeto or something. And this is also going to take self as its first parameter. Then we instantiate this class, let's call this tiger is equal to and animal. And then we can make that tiger. We can make an IIT. We could make it Chase, we could print out its fur color or we can make it, let's do this. Def speak. It's gonna take self and this is simply going to print rar, rar, animal dot speak. And so when we run this, when we run Python lesson for class clean up, it's going to print out rar, just as we expect it now this is nothing that we haven't covered already. And if you're just stepping into Python 301 and you're sort of new to Python and you're just wondering what this passes. This simply means don't do anything but allow indentation. Because if we don't have this pass in here, let's go ahead and see what kind of indentation error we're going to get. We get an indentation error expected an indented block after deaf Chase. Actually it's looking for it in here, doing its best to figure out where it is. But we simply put pass in there. Now this is a very generic class and there's honestly nothing super useful about this. But in the next lesson we will talk about class inheritance. So there's no task for this lesson. I just wanted to show you that I'm going to clean this up a little bit. In the next lesson, we're going to extend this animal class and we're going to create, let's say, an actual tiger or a different type of animal. And then we can overwrite the speak method, the method chase method. We could add all sorts of things to it. So when you're done watching me clean up this code, let's head on over to the next lesson where we talk about class inheritance.
6. OOP Class inheritance: That's inheritance. So one of the really nice things behind Python, or I guess really any class in any language, it doesn't have to be python. But the nice thing about a class is that we can extend it. And so instead of just saying class animal and then have another class called tiger and another class called house cat. We could, instead of having to rate, speak, eat chase for a color over and over and over again, we can simply extend it. And so what I'm gonna do here is get rid of this and create a new class. And this one's gonna be called tiger. And in brackets, it's going to extend animal. And I can take pass in here to literally do nothing. And it's automatically because it's extending this animal class up here. It's automatically going to get the fur color, speak method, eat method and chase method. So what I can do is now write tiger is equal to a Tiger class. Tiger doublespeak, and instead of writing, animal is equal to an animal class or tigers equal to an animal class. We can now say Tiger is equal to a Tiger class and then the tiger speak. So let's run this code and see what happens here. Run less than five. And it says rawr. Now just for fun, let's go ahead and print this out. Let's see what kind of type this is. Print type, tiger. And it's going to say rar, but it's also going to say main tiger. This is different from animal. If we swap this out for animal, we're going to see that let's us as RAR or the class main animal. And so we swap that out. Now, what's nice about this is we can overwrite everything in here. So instead of saying Speak raw or we can say something else. Deaf, speak, it takes self. And what is this tiger we were going to say, there grew right? Like Tony the Tiger and get it. Ok. Maybe not the greatest joke I've ever made. But let's go ahead and run this and we're going to see that actually let's get rid of this line. We don't need that anymore. When we run this, it's no longer going to say rar because we're instantiating the Tiger class. It has speak. It's inheriting class. It also has speak. Which one is going to run. Well, because we're instantiating the Tiger class and we're overwriting speak, it's going to run this one. So it's going to say their rate as expected. Let's go ahead and create one more example and let's create a house cat class. It's also going to extend from an animal. And when we overrate speak, we're gonna make it say. And that's all we're going to do there. And instead of tiger, tiger speak, we can still keep that instantiated if we wanted to. It's not doing anything so we could actually delete it by. Let's, let's show you an example where we have both a tiger class and a house cat class. So we can now write cat is equal to house cat, cat dot speak. And let's also write tiger, doublespeak. And one's going to say they're great and the other one's going to save me out. And let's go ahead and run this. And this is the rule and meow. Now at this point in time, you're probably thinking, well, this is no good. I mean, I might as well write a regular house cat class and not extend from animal if I'm overriding the one thing I'm using anyways, if that's what you're thinking. Yes. You're absolutely right. Let's go ahead and look at that for color though. Let's print cat dot for color. And we're going to see that this is orange. They're great meow, and it says orange. But we can overwrite that. We can say the fur color is equal to black. Let's save that and rerun this. And it's now going to say black. And so by default, it inherited orange as its fur color just as a default. So it's kind of like a blueprint. And then we changed that for a color for this particular house cat to be black. Now essentially what we're doing here is we're creating some sort of blueprint with this class, and this is called an interface. In the next lesson, we're going to talk about interfaces and sort of what they are and how we implement them and the reason we even write them. But for now what I would like you to do is create a class called animal. You might already have one. And then I want you to extend that class. I want you to inherit that class. By writing class your new class name. Extend animal by putting animals in brackets here in parentheses. And then you can overwrite it attributes or its properties. You can also overwrite its methods. And then I want you to instantiate that house cat or whatever animal you choose to use. And then whatever method you chose to overwrite whether its speak, eat, maybe something brand new. Maybe it's something that's outside of what you've seen in this module so far. I want you to execute it. Cat dot speak. It's going to print me out. And then I want you to, as you have overwritten one of your properties in here, I will need to also print out that property. And so here I just did print not for color. And again, it's really, really important that you learn how classes work. Because in frameworks such as Django web framework, we're going to be using a lot of classes. And it's important to know that even if you didn't know what animal Hamlet's zip that up there. Just collapsed that so even let's say you don't know what's inside of animal. You would be able to somehow overwrite what is inside of animal. You, you would be able to explore and figure out, oh, an animal has a fur color, but I want to overwrite that. It has a speak method. Ok? Yeah, yeah, it's got a default speak method, but I want to overwrite that too. So when you're done that, let's head on over to the next lesson where we talk about class interfaces.
7. OOP Class interfaces: Let's have a conversation around what an interface is. So essentially, if you wanted to boil it down, a class interface is literally just a blueprint. And so all it's saying is there's going to be some sort of property. There are going to be some sort of methods or just a method and it's simply supposed to implement something. And in the last lesson we created a tiger and a house cat. Let's go ahead and just delete these just for a second. We'll create new ones. And let's work with just this animal. And typically when you see an interface, you're going to see that there's some sort of method and it's just going to pass. Or you're going to see something like raise, not implemented error. And let's give this a better example here. Let's extend animal, let's do class. House cat is going to extend the animal class and simply pass. That's then instantiate that house cat. So we can say house cat is equal to host Kat or cat is equal to house cat. And then we could do cat dot. And which one did we overwrite here? Well, we didn't overwrite anything, but we know that it's going to automatically inherit speak from the Animal class. So let's go ahead and speak. And let's run this file. This is lesson six class interfaces, and it says not implemented. Now the idea here is that you simply have a blueprint and that's when you use this. You're supposed to implement this. If you want to use it, you have to implement it. And so to get rid of this not implemented error or if it's just passing is returning none. We could simply say def speak. It's going to take self as its one and only parameter, mandatory parameter inside of a class. Print, meow. And so what this is doing here is it's saying, hey, house cat is going to use the animal interface or this blueprint. If we try to use speak well, there's nothing implemented, so we have to implement that. And because we extended animal, we overwrote speak so that this no longer works. So you can kind of think of it like this. We take these two lines and it's a lot like this. Boom, we simply replaced it. But the interface or the blueprint is simply meant to tell us that it's supposed to be implemented. So when we save this and rerun this, we're not going to get a not implemented error. We are now going to get print as expected. And so that's pretty much the only role of an interface. And interfaces is a blueprint that says, hey, if you want to use the speak method or any method, you have to implement it yourself. But just so you know, whenever you extend from it, house cat will always have the speak method and it's our job as developers to overwrite it. Now, your task for this lesson is to create an interface. So if you already have an animal class, if you've been following along with Python 301, you probably haven't animal class already. Go ahead and instead of using speak, Go ahead and raise a not implemented error on eat or chase, or another function or method of your choice. Extend that class or inherit that class, create a new one. I just created one called house cat, and then overwrite that method. You might have already done this once before. It's not a bad idea to do it again. This time we're implementing an interface with not implemented error. Next up we're going to talk about the super function.
8. OOP The super method: So a lot of the time you're going to see with classes that because we can extend a class, you're going to, you're going to be able to access a function above it. So think of this as below and animal is above. And so if we think of this as a hierarchy, which is how I like to think about it. We're simply overriding speak, which is what we did in the last lesson. But if we wanted to, we could execute whatever is in here. So instead of overwriting speak, Let's overwrite eat. So in our animal interface, let's go ahead and print. I am eating. Yum, yum, yum. And because we're extending house cat from animal, We can now instantiate that house cat and we can say eat. And we've done this a few times already. So we're not going to see anything new in here. It says, I am eating yum, yum, yum. And even though house cat doesn't have a method on it, the classes inheriting from animal, it has it. So we're allowed to use that. Now let's say we wanted to overwrite the eq method, but we also wanted to execute the code that's in here. Let's do two examples here. Def, eat self, print. I am eating salmon. And when we re-execute this cat dot eat, well we've overwritten the original eat interface method. And so all we're going to see now is I am eating salmon. But what if, what if for some reason we wanted to also execute this? What we could do is we can sort of bubble it up. So remember this is the bottom, this is the top. We can bubble up to the animal class and execute eat. Now there's two ways to do this. There's the right way and the wrong way. The wrong way is to write animal dot eat. Because this isn't instantiated in a variable yet or anything proper. I mean, we can access it technically, but really we shouldn't do it this way. And then the right way is to simply say super. It's a function dot eat. And if there are any sort of extra parameters in here, we just simply match the parameters. We don't have any parameters, we don't have to pass in self. It's in a class that already knows itself is being passed in automatically. And so what this is going to say is get the class that's above it, the Animal class, that's what super is doing here. And then execute the eat method. So it's gonna get that animal class than that eat method. And then it's going to execute it for us because we said eat with parenthesis, right? They're executed. It's going to print, I am eating yum, yum, yum. And then it's going to print, I'm eating Sammy, let's go ahead and save this and give this a try. And we're going to see it does exactly what I'm describing here. I'm eating yum, yum, yum, I'm eating salmon. And so all this did again was bubble up to that animal class. Execute dot eat. Whatever is in here could be a print statement, could be anything. It will execute everything in there. And then we wrote whatever else we wanted. And again, this could be any logic that you want to write whatsoever. It doesn't really matter that it's a print statement. It could be mathematics, it could be a web scraper. It could be anything you want. Now let's go ahead and do one more example with chase, but let's add some keywords in here. So in our animal class we have deaf chase, self. And what are we going to chase? We're going to chase a gazelle. That's what we want to chase. Let's chase and animal. And this is going to print, I am chasing an animal. And let's make sure that animal by default is a gazelle. I hope I'm spelling that right, maybe not. And when I go down here, Cat ab.js is now going to, because we haven't implemented it yet on our own. It's coming from the interface. It's going to say I'm chasing whatever the animal is, which happens to be a gazelle by default. It's go ahead and run this. And it says I'm chasing a gazelle. Now we can also pass in a key word in here. The cat might be chasing a mouse. Let's go ahead and run this. And it says I am chasing mouse. Funny English there, I'm chasing a gazelle or a mouse or something like that. Now to implement Chase, we can do, to do, to do, we can write def, chase, self, animal. It's going to match what we see up here. The animal by default is going to be gazelle, but we're always going to pass in an animal, so we're not going to make this keyword argument. We're then going to say super dot chase. We're gonna pass in that animal up here, whatever that's going to be. So it's going to say I'm chasing by default because l, but maybe it's the animal is chasing a mouse. I'm chasing a mouse. And then we could print animal was caught and we could use f strings if we wanted to, or just a regular print statement. It doesn't really matter at this point. These are just examples. Let's go ahead and run this. And this is going to run cat dot Chase, which is here. It's going to super up to this one. It's going to say I am chasing a, whatever the animal is. By default it's gazelle if it's not provided. And here we are saying it is required. And then we're going to pass that in. Let's go ahead and run this. And it is missing a keyword in here. So let's go ahead and throw mouse as a keyword in here. And not a keyword, but a required positional argument. Let's go ahead and run this one more time. And it says I'm chasing a mouse. Mouse was caught and so chases now doing whatever the interface is telling us is going to do. We then bubbled up using the super method or the super function. We said, hey, wherever this is extending from whatever class that's extending from US that then run the chase method and we simply passed in animal. So animals actually doing a lot of traversing here. We pass in animals a string here. It's going to then go into here, then going to go into here. It's then going to be overwritten here. So instead of gazelle is going to be Mouse and we're simply going to print it. So we're moving data around quite a bit. And this is very common in Python. It's actually common in all class-based languages. So that all happens before it even gets to print animal was caught and then it prints animal was caught. Now, this is somewhat of a tricky concept to wrap your head around. So what I would like you to do is create a brand new interface class you can call animal just, I would like you to write it from scratch. Just as good practice, you know. So create like an animal interface, make it chase something and it simply does a thing. Then extend that animal. I extended my to be house cat. Yours could be a tiger, a cheetah, an elephant. It doesn't really matter. Just make sure you extend it, overwrite that Chase method and use this super function in it. And remember super simply going to say, oh, okay, this is extending from a class animal. That's our interface that's up here. And then it's going to say chase. So it's now talking about this animal that's super chase. That's this method here. It's going to then print, I'm chasing an animal of some kind, and then execute some code underneath it. We're just using simple print statements, but you could actually use proper Python logic if you wanted to. You could add numbers. You can scrape a web site, you could do pretty much anything. We're just sticking with something fairly simple to get our heads wrapped around the idea of object oriented programming. Once you've done that, let's move on to the next lesson where we talk about defender methods. And under method essentially lets us take automatic action. And for instance, our animal always has a fur color of orange. But what if we're using an elephant? Well then we'd have to write the first color in here. I guess is going to be gray. Or we're going to have to take that house cat. And we're going to say cat, fur color is equal to gray. And at this point we're just working with variables. That's no way to live your life. We might as well throw it into here when it's being instantiated. We'll talk about that in the next lesson.
9. OOP Dunder methods: Let's talk about defender methods. And so what I'm saying here is d, u and d are dander and what this means is double underscore. And so these are magic methods that come with the class. If you're already familiar with classes in another language, you're going to be familiar, for the most part with dander methods. And we're magic methods as they're called in some other programming languages. So let's move back up to our animal here, and let's create a defender method called init. And it starts with DEF underscore, underscore IN IT, underscore, underscore. It's a method, so it always takes self as its first parameter. And then we could give it any other parameter. And so this could be for color. And then it can do a thing. And what this allows us to do is enter code down here, which we're extending. We can now put a fur color in here and let for color could be gray, for instance. Now, currently this is not going to do anything, but if we execute this code, we're going to see that there is not going to be an error. There's just no error that's gonna show up. It's gonna be nice, cool. It's doing exactly what we did in the last lesson. But what we're saying now is we can pass in a positional required argument. Let's go ahead and run this without gray in there, even though init is accepting for color. And we see missing one required positional argument. Now let's back up a second. What is a knit doing here? Init is simply saying when you instantiate this class, when you activate this class, even if it's being extended or inherited in another class, we now need to pass something and this is going to be run before any other function. And so whenever we use underscore, underscore init, what this is saying is throw in some sort of parameter or parameters or arguments, and that's going to automatically execute so we don't have to then type cat underscore, underscore init, gray, something like that. It's just going to automatically do it for us. And so we can type gray in here. And let's go ahead and let's demo that this is going to execute before all of our other functions. Print for color is for underscore color. And let's use an F string here. And it says for color is gray, then it executed, I'm chasing a mouse. Linda executed mouse was caught. And so there's an order of operations here. Now init is by far the most common one you're going to see. And this just simply means that we can work with for color. Now by default that for a color is going to be orange. Let's go ahead and get rid of that. We don't need that anymore and we can simply, because we know that this is going to be executed first, we can simply say self dot for color is equal to for color. And what this allows us to do is then use self.view colour in any other method that we want to use, whether it's in the animal class or it's in and inherited class or an extended class, it's going to automatically assign that for us. So let's get rid of that print statement. And let's create another method in here. Def get for color self. And let's simply print self dot for color. Now, for the most part, you can ignore all of these because we're not going to be using them in this video. What we're going to do is create a class. Automatically throw in the fur color is going to then assign self.view color. We can then, once we instantiate this class, we can do cat dot get for color. This is a method, so it takes parentheses. And what that's going to do is because there's no get for color method in our house cat, but there is one on the animal interface is going to execute this, and it's simply going to execute whatever color we put in here. And just as a better example, let's write this out. Getting for color. And this is going to say getting for color. Gray. Says getting for a colored gray. Let's do orange or an ACH or ran. And it's gonna say getting for a color orange. And so what's nice about this is now we can use the super function. So we can say def in it on our extended class. Self for color super due to do init. And that's going to take the first color as well. And so what Anytime we instantiate a house cat, like we're doing down here, we're going to overwrite that init function, this one up here. But instead of just overriding it, we actually wanted to do everything into doing by default. Let's say we were happy with that logic. We don't want to overwrite it, we simply want to extend from it. So we're going to allow self.view colored to be assigned. And then we can, if we wanted to print something like fur color was saved to the class object. And then we could do something else in here if we wanted to. But you're gonna see this a lot too. In professional level Python, you're always going to see and init or not always, but a lot of the times you're going to see a net and sometimes you're going to see us overwrite it. And so we're overriding it here and we're saying actually, we do want to override it, but we also want to use the regular logic that comes with it, whatever that may be. And then we want to maybe do something else. So let's say we didn't necessarily want to overwrite the animal type everytime. We could say up here, animal type is equal to unknown. And we could force that animal type in the house cat self.age animal type is equal to host Kat. Let's go ahead and print self dot animal type. And so at this point, we're now using init. We're using a Dundee where you're using super regular print statement. We've seen that a trillion times already were then forcing that animal type to not be passed into init. It's not in there whatsoever. By default, it's unknown. But because it's a house cat, we want to hard code this. We wanna say house cat is always going to be house cat. No matter what we do, it will always be host Got It is not changeable. And so we do not throw house cat in here. We're simply saying instantiate house cat and it's going to have an animal type of house cat in there. And it's going to print that out and run this. And it says for color was saved into the class object. That's right up here. It ran super up there. So we do, do, do, do, do. We know that self.view color is accessible and we have access to that in all of our methods. Then we said the animal type is a house cat and print that animal type. And so first print statement, house cat is the second print statement. Where is it right there, it says house cat, and that matches down here word for word, letter for letter. And then we're getting the fur color and we're saying it's orange. And that's coming from here due to, due to do get for color. And we simply set that for color in a knit. This is getting to be pretty advanced. Python to be honest. But we're passing it in here saying self.age for color. And then in a method we're accessing self.view color. Now, if you're looking at this video, scratching your head, going cool. I don't understand a single thing that was just said. What I would like you to do is try this out. I want you to try this out until it makes sense because you're going to see this everywhere, especially, especially in professional level Python. This is very important that we understand both super and dander methods. Now in this lesson we're really just talking about the dinner method and the method in net as a recap, simply means execute this before every other method inside of our class. We can override it. We can use super on it. We can do anything we want. It's a lot like a regular method, like speak. It just happens to run first automatically and those parameters get thrown right into the class instantiation. And so now this looks a lot like a function with a parameter. The only difference is this is a class and this is automatically going to be accessible throughout the rest of our class. I want you to give this a try. Please spend at least ten minutes on this and just tinker around with an even, even if you can't get it to work, it's good to spend that ten minutes on it. And just because how brains are designed, even if you struggle with it, it's okay. It's good practice regardless. Go ahead and give that a shot for about ten minutes. If you don't get this working in about ten minutes, feel free to move on. You'll have lots of time in the future to practice using dander methods and super.
10. Errors and exceptions: Welcome back. Let's stop talking about classes. Can we spend something like eight lessons together talking about classes? And yes, they are important in Python, they're extremely important in Python, but there's another thing that's an extremely important in more advanced Python, and that is how we can manage error flow. So we do this thing, try a thing, except another thing. And what this is simply doing is say, try a block of code. And if there's an error, do something else. So it's a lot like an if else statement. The only difference is it looking for errors. And so let's take a look at a simple tri-accept. And so the syntax looks like this. We're going to try a piece of code. And so let's try. Total is equal to one divided by 0. Now if we go into our Python shell and we do one divided by 0, we're going to see this gives us a 0 division error. You cannot divide by 0. Mathematically, it's just one of those roles were not allowed to do it. So what we can do now is accept any sort of exception. And we can say that total is going to be 0. Print the total. And this is really all there is to a try and except block in Python. The important thing, in, the important thing here is that it's going to try a piece of code. If it fails, it raises some sort of exception like what we see here, a 0 division error. It's then going to skip this line of code and everything below it. This will not execute simple as that is then going to pop out of this block and then going to then try to execute the exception. And so when we run this Python lesson nine, errors and exceptions, I didn't spell exceptions right, by the way, I have to fix that. That's going to bug me. Exceptions better. We see the total down here is 0 and that's because it's getting 0 here. And as an example, let's do this. Print trying one divided by 0. Let's move that up to the top. And then let's print something underneath. This will not show up. And let's print one more in here. Exception was caught. Let's go ahead and run this one more time. And it says trying one divided by 0. This will not show up, did not show up in our script down here. And it says exception was caught. Now at this point in time, you should be thinking, oh, geez, where am I ever going to use AS I hope you're asking that question and if you are asking that questions, let's move on to a proper example here. What happens if we ask the user for some input? So ask for a number. We're gonna say input. What is a number? Then we're going to make sure that this is always a number. So we're gonna say num is equal to, and we're going to cast it as an integer. So no matter what someone types in here, it's always going to be a number, it's going to be a full number. And let's print that, print num. And when we execute this, what is the number? Let's write ten. And it just has ten. Cool. But what happens if someone doesn't write a number? Well, that's still going to get stored in this variable. We're then going to try to cast that number as an integer. And what if it's not an integer? What if we write Python 301? We get a value error. Invalid literal for int with base ten. Basically what that's saying is it cannot turn a string into a number with a base of ten. But the TL, DR, the short story here is it's simply failed. We could not print this out. We couldn't work with this. So what do we do? We ask for a number. We try to cast that as an integer. If it doesn't work, we can do except all exceptions. We can then say that number is going to be unknown and simply print it. And this is going to allow us to print that number, which it didn't do before, because it errored out and it's going to say unknown. So let's give this a try. What is the number? Python 301. And it says unknown. If we execute this again, given an actual number, it says the number is ten. And so what this did was it said, oh, actually, we can't manage his value error. Python just doesn't know how to turn a string into a number properly, in a way that's predictable for you, the programmer. And so then it throws an exception. We catch that exception and we overwrite num to be unknown, and then we just printed it out. Now this is a really good use case because what if you're trying to do something and it doesn't turn out the way you expect. Now what I would like you to do is try this out. You can either try with the first example. This one up here where we tried to divide one by 0, simply doesn't work. Or you can try this example, which is the one I prefer because it's closer to being a real-life example. Ask for some input. Try to cast that number as an integer, catch the exception, overwrite the number and then print it out. And the next thing about this is we're getting into simple error handling and error flow. And again, what's important about this is in our first example, if we look here, our code simply died. There was no way to recover from that. And so all we're doing with a try-except IS gracefully catching that error and managing it so our script doesn't die so that our code can continue executing the way it's supposed to execute. Go ahead and give that a try. It's a pretty straightforward example. So this should only take you a couple of minutes when you're ready. Move on to the next lesson where we talk about catching some new exceptions. We're going to catch particular exceptions.
11. Catching exceptions: Let's talk about catching particular exceptions and figuring out which exception is throwing and how we can manage multiple exceptions. So let's create an example here, and this is just going to be recreated from the last example. We're going to say some sort of number is equal to input, enter a number. Then we can try to change that number into a number, because when we ask for input, it always comes back as a string. And we can accept that exception by doing anything we want. And let's just say this number is going to be unknown. And let's simply print the number when we're done with it. Let's go ahead and run this script. This is less than ten. Enter a number, ten, prints ten, enter a number. Python. It says The number is unknown. In the first example, we entered a number ten, it came back as a string. And int was able to typecast this to an integer. So that worked perfectly fine. In the second example, we typed Python. Essentially this is what we did. Now we can't turn a word into an integer, just doesn't work. Python doesn't know what Python, this string is opposed to turn into as a number. Not in a predictable way anyways, so it throws an exception. Now when the exception is thrown, we said that number is going to be unknown. Now how do we know what that exception is? We can figure out what this exception is by typing except exception as E. Print exception was caught. And then we can print type of E. Let's go ahead and run this again. And let's type python as our number. And it says exception was caught class ValueError, and it's unknown. So now what we can do is we can accept specifically a value error. So we can add another exception in here except a value error. Print num was not a valid number. And let's try this out. This has multiple exceptions in here. So let's type Python has a number, and it says Python was not a valid number. And then it printed at it down here. Python is what we wrote. Could not cast it to a number or an integer. Then we said whatever that value was is not a valid number. Now this is a lot like an if else, if, else if statement. So it's going to try something like an if statement. If it then catches this particular error, it's not going to run any other exception in here. It's only going to run this one particular exception. And so now we have a way to deal with a specific value error. What if we wanted to then do something with this number? Let's say num was perfectly accepted, lowers the number ten, and then we wanted to divide it by another number. So let's create a second input. We'll call it num to enter a second number. And we're going to divide these. Then we're gonna say num two is equal to int num two. And the total is going to be NUM divided by m2. Now if num1 and num2 are both able to be cast into an integer. Total is going to run. However, if one of these cannot be cast into an integer, it's going to then throw a value error. We're going to then catch that value error with an exception and print whatever number was not a valid number. And in fact, it might not be numb. It might be numb to. So let's say numb or numb to were not valid numbers. And let's try this out. Enter a number. Let's do 100. And your second number, ten. Okay? No errors. That's good news. What if we did the first number to be 100 and the second number is 0. Well, in the last lesson we tried to divide by 0. We can't do that. And so we can see that the exception was caught. It didn't execute a value error because no value error was thrown at us. So we can't catch that error. But there was another error or another exception that was thrown at us that we can then except then it prints exception was caught. Print the type of the exception. And we worked with a number, which we actually don't need to do anymore. We might want to work with totals instead. And I don't know. Now when it prints Type II, it says class ZeroDivisionError. What we can do is catch that on its own as well. We can say except 0, division error, print numbers could not be divided div I did. And let's get rid of that. Let's try this again. Enter a number ten and her second number 0, and it says numbers cannot be divided. There is no value error thrown this time. That means both of these were able to, both these numbers, num1 and num2 were both cast into integers perfectly fine. So there was no value error to catch. So it skipped over this. But then it said total is equal to numb one divided by num2, ten divided by 0. Well, you can divide anything by 0, we get a 0 division error. So we said, okay, try to catch a value error if it exists, if it doesn't, let's try to catch the ZeroDivisionError. We then caught that ZeroDivisionError and printed something else out. And because this one was executed, the generic exception at the bottom was not executed, this code was simply skipped. So Python is going to pick one of these exceptions, whichever one is the right one. And it's going to execute one of these avenues of code. It will always, always, always try to execute this code. And if there's any errors, it's going to check to see if it was a value error. Check to see if it was a 0 division error check to see if it was a general broad exception of some kind that we aren't explicitly catching. And so at this point in time, we are now catching a value error, 0 division error. Or if there's some kind of other air that's unknown to us, we can then work with that. Now, I say try and catch because I come from other programming languages as well. In Python we say try except it's the exact same thing, just a different keyword. And you're going to see try except all over the place in professional Python. And the reason for that again is because we want to try something. And if there's an error, if we don't try and accept that error, our code is simply going to die. We don't want that to happen. We want it to keep executing, we want it to keep on, keeping on. And so we gracefully catch each one of these errors, or we broadly catch every error, every exception, and work with it. And that just means that our code can keep working. It's not going to error out on us. And that means all of our code down here will still execute. And that's good news. Because what if you're building a program like Google's web scraping bot, and it gets to a website and it's trying to scrape a particular email address and verifying email address. And maybe that email address doesn't actually exist. Like it's, it's it passed. We thought it wasn't email address, but it's not a real email address. Well, if Google Boston didn't catch that error, it would simply die and then someone would have to go and restart it. And you'd have someone at Google all the time just typing Python scrape internet, Python scrape internet, Python scrape internet, Python Stream Internet. And our job as programmers is to if we can automate ourselves out of a job. And so it's going to then maybe try to get an email address or another website. It's going to then try to do something. And if it can't, maybe it skips it. Maybe it tries a different chunk of code. Maybe it tries to do something else. And then we don't have someone at the other end of a computer typing Python. Scrape internet dot py, Python scrape internet dot py, Python scrape internet dot py. Every time at Arizona, it just automatically going to handle it for them. And that's the power behind try and except is you can accept any sort of error. These ones just happened to be keyword errors thrown exactly from Python. We can create our own as well if we wanted to. And then we can gracefully handle an error. And that means our code will run forever without any problems. Now what I want you to do, I want you to try this out, write some sort of example, and you can steal my example if you like, where you try some code and you except one error, one specific error type. Then you accept another error type. And then you accept just a general areas. Even Make sure you print out type E so that you can deal with that later. Go ahead and write this complex tri except block, and when you are ready, let's head on over to that next lesson. Good luck, and I'll see you over there.
12. What are decorators and how to create one: Let's talk about decorators. Now if we go back to a particular lesson, it's one of these methods. Yes, we can have this thing called a decorator. And a decorator is simply a function that wraps around another function. And in this lesson we're going to create our own Python decorator. So let's get out of this and create a new Python file. We'll call it less than 11. Deco read tours that corridor. So typically we have some sort of function, myFunc. And this is simply going to print my name is Caleb. Then we can run myfunc like this. This is Python, one-to-one stuff right here. Cool. This works very bland, very boring. This isn't in a class. This isn't in a try. Catch us isn't doing anything new. But let's say we wanted to write a decorator which typically looks like this. Deck or a Tor has the at symbol and then the name of a function. And all that's going to do is execute a function called decorator and throw this function inside of it so that we can execute something before or after automatically. So let's go ahead and create a decorator. And so we can create my decorator. And this is going to take a function just like myfunc. Myfunc is going to be thrown in there as an entire function, not as a variable, but has a function. So we're passing functions into functions at this point. Then we can create some sort of wrapper and we typically see the word rapper. This is very, very common. Print. Do something here. Then we can execute that function, whatever that is, print original function is finished. And let's simply return the rapper. And so this is a function inside of a function. We actually saw this, I think it was in Python 201, where we can put a function inside of a function. And this was essentially a decorator at that point in time. And so what we're saying here is my decorator is going to take a function as its only parameter. It's then going to register a new function called rapper is gonna do a thing, execute that original function. We're talking about weird scoping at this point in JavaScript, this is called a closure. Execute said function, whatever that function is, and then print another statement. Lastly, we return the wrapper. So now there's two ways to decorate a function. Let's go ahead and get rid of this up here because we know that myFunc is simply going to print my name is Caleb. We could call this new function is equal to my decorator. And because it takes a function, we're gonna throw in myFunc. Now notice that we don't have the parentheses here. We're not executing myFunc where simply throwing it in as a function by itself, not as an executed function, just as the function object. Then we can say new func, execute this as its own function. Let's go ahead and try this out. And it says do something here. My name is Caleb, original function is finished. And really this is all there is to a decorator. Now let's go ahead and do this in a more Pythonic way and more modern wireless. Cut that and let's move it up top. And let's decorate myfunc with my decorator. And because we're using this sort of syntax, we don't need to wrap this in here anymore. We can simply write my func. Let's go ahead and get rid of that. And let's type myfunc. Myfunc, my function here is decorated with my decorator. It's going to pass in this automatically. That's going to register wrapper, print a thing, do a thing, print the original function is finished. Return that wrapper. Let's go ahead and save, and let's run this. And it works the same way now this is very Pythonic. I'd like this way better. You'll, you're going to see this way probably more often than not. And all this is, is the exact same as that first function. Not the function, but the first example. And all we're saying is hey, use the at symbol here as a decorator. Wrap my decorator around my function. So it's going to execute my decorator in somewhere inside of there. It's going to execute the original function. And then we can do something before or after or nothing if we don't want, we can literally do nothing. I mean, that would be pointless of decorator to do nothing. But we could do something before and after, which makes us really, really powerful. Now why would we ever do this? Well, because sometimes we have a function and we simply like what it's doing. And we just simply want to add a little extra logic to it, a little extra functionality. And so we don't necessarily always want to change our original function, but maybe we do want to take that original function and extend it. So it's a lot like class inheritance, where we took a regular class and then we inherited it into a new class or extended it into a new class. This is taking a function and extending it with my decorator. Now this is a very, very, very simple decorator, but it gets the example across. So what I would like you to do is I want you to create a decorator and I want you to use it this way. Don't use it the function based way, the original way which you are going to see from time to time. But this is the more Pythonic way. So create a function, call it my decorator. Its only argument is going to be the function itself, not the executable function, just the name of the function. And so basically we've reassigned a function name as a variable in here. Inside of that, you're going to need to register a nother function. Do a thing, execute that original function, do a second thing, and return that internal function. So we have functions inside of functions. You want to return that internal function, then create another function, just a regular one. It could be a print statement, it could have some actual logic. I could do anything you want and decorate it with at my decorator. At means it's a decorator. My deco rater simply matches the name of the decorator function. Go ahead and execute myfunc with and without the decorator. For example, if I get rid of that decorator and run this, it prints one line. If I put that decorator back and re-run this, it prints three lines. I want you to go ahead and give this a shot. It's not too often you're going to actually create your own decorators. You're more or less going to be using them. But even the possibility that you get into like open source or really advanced Python, then you're going to be creating your own decorators. And it's really important to know how these work and the order that things happen. For example, we have something happening before our function, after our function. Go ahead and try that out and when you're ready, let's head on over to our next lesson.
13. What are generators and how to create one: Welcome back. Let's talk about generators. So a generator is, is really, really interesting concept. So for example, if you have a list, so LST is equal to 123123 are going to be stored in memory at all time. Now let's say this list has 10 million numbers in it, which if you're getting into data science, a could have 10 million numbers in it and you don't want to store all of that in memory because then you have to crank up the RAM on your computer. And your computer is going to need to, basically your computer's gonna get really expensive if you just throw hardware at it. A generator is a software way of saying do something with the first number, then do something with the second number, then do something with the third number, and he doesn't care what came before or after it. Now a generator is typically put into some sort of function. So we could say def myfunc for some sort of number in a range, ranges already a generator in Python three. So 14, for example, print num. And when we execute myfunc, Python Lesson 12, That's not going to do anything because it's in the function. Let's try it one more. There we go. It says 012345678910111213. Okay. Not bad. You know, we're only working with 14 numbers. This is not technically a generator. But let's say we wanted to do something big, like make this always return all of these numbers to the exponent of itself. So we would do numb to the exponent of num. So for example, ten to the power 1013 to the power of 13. And instead of return, which is only going to execute once, like, let's try this out. That's not going to work. Let's do total is equal to myFunc and then print the total. Shows up with just one number. What if we wanted multiple numbers? What if we're working with really big numbers? One said we could use the keyword called yield. And what yield is going to do is it's going to say this function is now no longer just a function hits a generator, is going to loop through every number from 0 to 13 and is going to yield whatever that number is. Two the exponent of itself. Now let's go ahead and print this. And we see when we print my func, it is a generator. Now again, remember generator is only going to work one number at a time. So in our first example where it was just a list, it knows about numbers 0 through 13. And in a technical way we can access those numbers. But if we're working with really big numbers, we don't want that. Or if we're working with really big data sets, we want to use a generator. So it's only dealing with the task at hand. It's not dealing with anything that came before or is coming after it. And so the idea here is you're, you're now writing code, that's memory performance. So if it's doing 14 to the power of 14, it doesn't care about 13 to the power of 13, it doesn't care about 12 to the power of 12. It's simply going to do this once. And when you try to access that number, then it's going to do the math. So it's kind of lazy. It's going to do the math only when it's demanded. It's not going to do it right off the bat. It's not going to try to store all of this in memory. The only thing that's going to do is store 14 to the power 14 in memory and then moves on to the next number. It's going to do 15 to the power 15 and totally forget about 14. Now in typical Python, let's say you're doing Python for web, you're not going to see yield a lot, but in data science you're going to see it a lot, a lot. And it's very important that we know how this works. Let's go ahead and rename this from myFunc to my generator. And let's get rid of this. And now we can say for num, let's not use them. Let's say Big num in my generator. And then we saw down here that this was a generator object. We can loop through these numbers and we can print them out to print Big num. And let's go ahead and execute this. Now, this is so fast with Python. We're not even going to notice what's going on. But what it was doing was first iteration, then the second iteration to the third iteration, then the fourth, fifth all the way down to the very last iteration. And you notice how these numbers are getting pretty big. It doesn't actually care what came before it or after it. And what's important about this is we can do with really, really big numbers. So let's do really big number like 50 to the power of 50. And let's see if this possibly crashes my computer. Look at that. It was able to do all of that quite easily. So I guess I was worried about it crashing my computer for no reason. But it was able to do all of this logic quite easily. And so the idea is, you know, when it got up to 49, it only worked on 49. It didn't have to remember what 4993 where it didn't have to care about what's coming next. It simply said when it was on 49. Work on 49 to the power 49 gives us this massive number here. And none of this needs to be stored in memory. Whereas if we had a list, we would have to have all of these numbers stored in memory. Now, numbers are small to store in memory, so it's not really that big of a deal. But when you're dealing with giant datasets, especially in things like machine learning or data science. You're going to want to use yield to make sure that your computer doesn't run out of. Well, basically thinking power, it's not going to run out of RAM. Now if you ever wanted access to these, let's say, we said, Let's just comment that out. And let's say we wanted to get all of these numbers. So all numbers is equal to my generator. And then we wanted to print these numbers for whatever reason. All numbers. Let's go ahead and execute this one more time. And it doesn't give us anything. It doesn't give us these numbers. Instead it returns a generator object. Now if we wanted access to these numbers, what we can do is cast whatever this generator is going to return the whole thing. All of these numbers. We can put that into a list. Let's go ahead and execute this wanted to being cast as a list. And we see all these giant numbers in here. Now at this point in time when we're casting this as a list, this generator, whatever it returns, all of these numbers is what it's going to return is going to be stored in this variable called all numbers. So now we're actually saving this for later. Whereas with just a regular generator, our previous example were not saving these numbers for later. We're just doing something with these numbers one at a time. But with a list. We said, yeah, okay, process all of these one at a time. And then all this saved values from that yield keyword goes into this list. And that list is going to be sorted in memory so we can work with this list down the road. Now, why we would do this, or where we would do this is if we're working with big numbers are a lot of processing. It can do a lot of processing one task at a time. It doesn't have to worry about all the other stuff before or after it. And then we can put just the answer into a list. So while the computer is doing 37 to the power of 37, we don't care about that processing. So basically we're saying this is a little more what's the word I'm looking for here? Temporary. It's little more temporary. And so or disposable adds a better word. It's going to do one thing. And then when it's done, it's going to throw it into that yield keyword. If it moves on to the next one is going to dispose of this in-memory. It doesn't really care what the last one was. It just cares about the current task at hand. Whereas with a list or any other datatype, once we stored in a variable, it's actually being stored in that variable. So to use a generator and then store it into a variable. Not really the proper way of using a decorator and not a decorator but a generator. This is more or less what we're going to use. So really it's a way to do some sort of processing and then throw it away. So we're going to say for every big number in my generator printed out, and it's going to process one to the power of 12, to the power of 23, to the power of three, all the way up to 50 to the power of 50. And it's going to print out one at a time. It's not going to store all of it in memory. It's just simply going to do one thing at a time. So it's really, really good for disposable code on large data sets. Now again, if you're getting into web development, you're not going to see generators all that often to be honest. But if you're getting into data science or machine learning, you're going to see this all over the place and a very, very important concept in Python programming. And in fact, we can do one more example here. Let's checkout range. So we can say total is equal to range of 50 and print total. And we're going to see that this returns a decorator as well. And actually it didn't return a decorator. It returned the function range 0 to 50. It is a generator behind the scenes though. And what we don't see is that it's not looping through and creating a list of 150. What if we did list range 50? We now get a list from 0 to 49 or 50 numbers in total. And so it works the same way as our made-up generator does. Now one thing to keep in mind is once you iterate on one of the generators in here, it's not going to work anymore for him. So let's go ahead and uncomment this. Let's get rid of this example down here, and let's rerun the script. And it should print all numbers. But then print is not going to work at all because there's not going to be anything in there. The generator has exhausted itself. It's a one and done kind of thing. And actually I'm just looking at this code and it's not going to, because it's going to create a new generator for every one of these. Let's go ahead and throw this into a variable. So my var generator is equal to my generator. And let's swap this out here for my var gen. And we can see that it prints all of these numbers, but it's not doing anything in here. So this generator, this function, we throw this into a variable. We then said, hey, execute this. So it says one to the power 12 to the power 23 to the power 349 to the power of 49, all the way through all those numbers sorted in a variable, that generator than exhausted itself. So if we try to access that generator again, we're gonna see that it doesn't print anything. And all it did was print our giant list here. But it didn't print out one at a time, kind of like that pyramid shape that we saw right up here. It's not going to print any of those because I generator is done. It's a one and done thing. So if we wanted to access the generator again, we have to, in a strangely worded way, access that generator again. And so we do it this way. We said call it ones up here and then call it again down here. And this time we're going to see different results. We're going to see all these large numbers. And above it we're going to see this list of numbers. And so one thing to keep in mind with the generator, it's a one-and-done thing. Once you exhaust that generator, it's done. You have to create a new one or not create a new one. That's bad wording, but you have to instantiate or generate a new generator. Once you've created that new generator, then you can do whatever you want with it. But if you try to execute that generator more than once, if it's like, for example, stored in a variable is going to be exhausted that first time and only cares about performing once. It's not like a function, a function and you can keep using over and over and over again. A generator is a one and done type thing.
14. Pipenvs: another type of virtual environment: All right, let's talk about Pip. Pip environments. I'm saying Pip env. And in Python one-to-one or Python 201, I can't remember. We used a python command called Python dash m dot v. And then we got inside of a virtual environment and this created a virtual environment for us. There's another way to do this, and there's actually several different ways to create a virtual environment. But another common way is a Pip env. And I personally really like Pip ends because they're nice and simple. And so we're gonna do this one in our command line here. That's make that bigger. Let's get rid of this sidebar here. And so I'm in my Python 301 folder. First of all, what I wanna do is pip install Pip env. And due to, due to do, it's going to collect Pip env. And today I have PIP nth. Now what I can do is type Pip env. And it gives me a list of commands. Check clean graph, install, lock, open run scripts shall sink, uninstall and update. This is a lot easier than writing Python dash m v, m dot v. Instead what we can do is type. And I'll clear this off. We can type Pip env install, and this is going to create a PIP environment for us. So let's just do pip install. Due to, due to due does a thing. And it says to activate your projects virtual end run pip, shell, Pip shell. And now we are inside of a PIP environment. You can see that I'm inside one because it has brackets over here. Let's go ahead and type python dash v. And we're going to see that I'm using Python 3.9, Cuckoo, cuckoo. That's nice. Let's get out of this by hitting Control D. So CTR plus D cancelled with Control C, Control D, that exits our Pip env. Now if I want to go back in, I do Pip N shell and I'm inside of it, then I can do pip install. Let's say I wanted to install Django is equal to 2, something. Doo-doo-doo-doo. Just lead it and solids getting 2.2.17. Pip show. Django. And I have two point-to-point seventeen. Now I know on my computer I don't have that version. I have django 3.1 something or other. And so this is now completely isolated from the rest of my computer and you can actually see location of where this is being used. It's in my users folder, Caleb Aeolian dot local share virtual EMS, Python three or one, that's the folder name. Then some sort of hash LIB, Python 3.9 site packages. Cool, let's get out of here with Control D. And let's say we want to get rid of this PIP environment. We could do Pip env, dash, dash or M. And just like that done. We don't have a PIP environment any longer. And so this is a really good way to create an environment and to delete an environment. Now, let's say you have a project using a certain version of Python. There's two different ways to use Python. When it comes to versioning. You can use pi n and you can switch your Python version. Or the way I prefer to do it is with Pip env. And I can do pip install dash, dash Python. And let's do Python 3.7. In our last version or last Pip env, we use Python 3.9. And so let's go ahead and use Python 3.7. Pip env shell. Let's just clear this Python dash V. And I'm using Python 3.7.2. So there's two ways to use multiple versions of Python. I personally prefer this way because when I'm done with it, I can simply say Pip env, dash, dash r, m, and that's just going to remove it. Boom, I don't have a PIP environment any longer. Now if we did ls dash LA, grep, the word Pip. We're going to see we have two Pip files. Now this command is actually going to be a little bit different. If you're on Windows, it's probably just D-I-R dir. You might be able to use grep. You can always just list out everything that's in your folder to you can do. Or if you're on Windows or ls dash LA, if you're on Mac or Linux. And I've got a Pip file and a Pip file dot lock. And what this is the PIP file.txt. And the Pip file specifies what kind of packages are involved inside of this Pip file. Now, I showed you when I do pip install Django, that it gave me Django. To point-to-point seventeen. The proper way to do this would be pip. Install its just create a new Pip env. And let's clear that. Then we can do pip install. Django is equal to 3.1 and it's installing. Now, the one downside to Pip is when you are installing things, it takes a couple of seconds longer. Now in my opinion, it's just a couple of seconds for a nice virtual environment, life is easier this way. Pip is faster than Pip enough. Some people have a problem with that. Some people do not. Let's do Pip env, shell, clear, Pip, show Django. And now we have django 3.1 in here. And we did that from outside of our virtual environment by writing pip install and then the package name is equal to the version. And again, the really lovely thing about this is we can do pip N dash, dash r, m, and it's going to clean up our PIP. And for us, this whole folder is going to be deleted so we're not wasting space on our computer now, if you're like me and you're running on a Mac, you might not have that much space and you might want to delete your virtual environments when you're done with them, if they're just like throwaway virtual environments. I do that all the time. Now I personally love Pippin. I like it more than a Python Dutch. And then dot ven. Ven. If it's just a lot to type, it's kinda weird typing too. Just because this is going to use the Python version that's on your computer. Now, if I wanted a different Python version in a virtual environment, I could do pip install dash, dash python, and then the version of Python I want. And that's going to work for me. Now the other way of doing this using different Python versions is using pi n, which is a great way to use different versions on your, on your machine as well. But I linked to localize my project so that everything is inside of a virtual environments. Because what works on Python 3 site, let's say 3.4 is not going to work necessarily on 3.9. And if I have a project using Python 3.9, but my computer only has python 3.8. Oh, now I can go ahead and use pip install dash, dash Python 3.9 and get that up and running. Now as little caveat, you might need Pi installed. If you need pi n, you can go and look at how to install Python on your computer. It's going to be a little bit different depending on your operating system. But I'm going to leave that in your hands because you are now an advanced Python developer. You essentially what I'm saying is you have the skills to figure this out on your own. And the whole lesson really is there's multiple ways to do multiple virtual environments. We can do pip, we could do Python, dash m, then we can use virtual n, we can use docker, we can use Vagrant. We can use all sorts of different layers of abstraction. I personally like Pip n. Now there's no task. I just wanted to show you this. I just want to show you that this is an available option for you moving forward in our next, and it's not even a lesson. It's our final project. We are going to be creating a banking app using classes.
15. Your final project: All righty, welcome back. Let's go ahead and create a final project. Now in this project, what I want you to create is a banking app. And it doesn't need to be super complex, but it does need to have a few little extras in it. So first of all, this should be class-based. It should have methods in it for with draw and deposit. And after every withdrawal and every deposit, I want you to write the transaction to a Python file. Now we didn't cover how to open and write to files and Python 301, that was a python 201 thing, if I believe correctly. So what I want you to do is I want you to try creating a project that's going to ask the user over and over and over again what they want to do. Do they want to withdrawal or do they want to deposit, and how much do they want to do? Then I want you to keep track of all of that inside of a class called bank after every transaction, right? That transaction to a Python file. So we have a history of that transaction, or not just that transaction, but all transactions. Now I'm not going to give you any more clues other then we're going to be using wild true. We're going to be using input. We're going to be using classes, methods, and properties. This should make a brain sweat. This is hopefully all brand new stuff to you. Well, classes, methods and properties should be brand-new to input. You should know wild tree, you should know, oh, what else do you need? You need to know open syntax for managing files. You should already know that one too. If you don't, definitely go and research how to create this. Now what I want you to do is try this on your own. Don't watch the rest of this video. I'm gonna show you how I do it. I've never built one of these by the way. So you're going to see all the errors that I maybe make as well. But if anything here is new to you, you don't know how to do it, you can't remember. I'm going to ask you to not refer to another video. Try the best you can. And if you get really, really stuck, Go to Google, go to Stack Overflow and look for your answer on there. Because 50% of coding is solving problems and you need to learn how to solve problems on your own. If teachers always hold your hand. Honestly, you're not going to learn. And I want you as my student to actually learn. Not just watched us because it's like editorial style, Netflix, you know, I want you to actually be able to create a Python app. And then, you know, maybe one day we can actually work together and how cool would that be? So go ahead and create a baking app from scratch. Remember it's going to have to ask the user for a withdrawal and an amount or deposit and an amount, keep track of all of that. What I'm going to do is fade this out and fade back in and I'm going to show you my solution. Alright, so first of all, I'm going to call this project dot pi. And I'm going to select all of that, comment that out and use that as a guideline for what I have to do. So the first thing I need is some sort of banking app. I need to create a class called bank. And what is my initial amount going to be? Well, I'm going to do deaf init self and the initial amount is going to be 0. That's how much I'm going to open this account with the $0. Use a float here, 0.0.0. Self.age balance is equal to initial amount. Then I need to withdraw and I need to deposit. So deaf, with draw self amount, I'm going to take that balance, self.age balance. And let's move this up. Self.age balance is equal to self dot balance minus the amount. Now what if this amount is, let's say a string for some reason. We can say the amount is equal to float amount. Now, we don't actually know if this is going to work or not. And so what we can do here is, oops, what am I doing here? Try to cast that as the amount except a value error. And that amount is going to be 0. And then we're going to set that New Balance. We also want to do deposit. Deaf D posit How much would always take self gonna take an amount. We want to do the same try and except here try. Amount is equal to float amount except a value error. If it cannot cast that amount to a float and the change that amount to 0. Self.age balance is going to be the self.age balance plus whatever that amount is going to be. So either we're adding to our account or adding nothing to our account. And when we withdraw, we are either taking away from that account or we're taking nothing away from that account. Let's make this one step smaller here so we can kind of see this in one view. And now let's go ahead and instantiate this. Let's do a count is equal to bank. What does it take in its init initial amount? Let's say I'm gonna open this account with $50 in it. 50.0050.50. Let's do that. Then I can do account dot deposit. And let's say I want to deposit $10 or $10 in there. And then let's print out my total amount. Account dot balance. And then let's say I want to withdraw 1475, not with a comma, but with a decimal. And let's print out that account balance again and just make sure we spell that right. Thank you. Vs Code for showing me a typo there. And let's open up a terminal and run this. And so we started off with 50.50, we added tens, we got 60.5, that's good. And then we took 14.75. So 60.5 minus 14.75 is 45.75. So that seems right, and I'm not going to check that math. I'm pretty sure the computer knows what it's doing. And now we need to add transaction log. And what else do we need to add in here? And we need to ask the user for input. So I'm going to make this. One step smaller, I accidentally hit the wrong button there. One step smaller. And let's do another method in your deaf log transaction itself. And then some sort of string. What are we transacting? What are we doing here? And let's just call this transaction string. Now we need to open a file. So we can say width, that's using a context manager here. And we can say with open transactions 2.txt, we're going to append to it all the time or create a new file. If it doesn't exist as file, then we can say file dot, right? What do we want to write? We want to write to the transaction string, and let's maybe always append a new line at the end. So let's use an IF statement here, an F Statement and F String, and throw a new line in here. And that comes from Python tool one, where we learned how to deal with files. So now after every withdrawal, we can simply say if there is an amount. Remember if 0 is or if the amount is 0, this is going to be false, so this is not going to execute. So we can say if amount then do a thing. Self.view, log trends action and this takes an amount, are not an amount but a string. It takes the transaction string. So let's call this withdrew. How many dollars the amount. Let's do the same thing with deposit here. And what I can actually do is Doo-doo-doo-doo copy that. And we just have to make sure that we swap out this. Always be careful when you copy and paste. So we can say the balance is equal to the balance plus whatever the amount. Whenever we deposit, log that transaction, we didn't withdraw. We visited the depositor, deposited the amount. And let's go ahead and open up our files here on the side. And we don't see transactions 2.txt. Is that what we called it? Transactions dot TXT? We don't see that in there yet. Go ahead and save this and run this and see if there's any typos or anything. And okay. Cuckoo, cuckoo. And it headed deposited amount. I did something very wrong there. What do we do in here? First of all, that's an F string. This isn't JavaScript. That's an F string. This isn't JavaScript. Let's go ahead and run this again. We see transactions. We'll look at that deposited at $10, withdrew 1475. Let's also add the balance in year with a new balance of self.age balance. And if I make that just a wee bit smaller here we can see that a, I have a typo and be that I'm using an app string. And in fact, we can get fancier With this. We could say something happened and instead of a New Balance, we could do tab, tab, tab balance. Is whatever that self dot balances. Let's go ahead and clear out to transactions dot TXT. And let's run this once more. Check transactions on TFC deposit $10 New Balance, 60.5, withdrew 1475 New Balances, 45, 75. Okay, this is looking pretty good. Now we need to create a, a loop of some kind to ask the user for input over and over and over again. Let's close that down. And we have all of our logic working. So let's go ahead and wrap this in something called a while loop. While true. Do a thing. What do we want it to do? We want to ask for an action, is going to be input. What kind of action do you want to take? Then we can say, if action is in a list of with Drell or a deposit deposit or I don't know why I wrote that, then. Do a thing. So then we can say if action is equal to withdrawal, amount is going to be what kind of amount due we want input. How much do you want to take out? And then we can do account dot withdrawal the amount. And I'm not worrying about typecasting here because we're doing it in here. Nope, not there, in here. And here. Then we can say else because the action is either going to be withdrawal or deposit. If that's going to be a deposit, how much do you want to let's say not takeout, but put in and account dot withdrawal is going to be accounted dot deposit d, z the amount. And then down here we can say print. Your balance is account balance. Now, we need to instantiate this before we get into the while loop so that we're not creating a new account every single time we iterate through the while loop. And let's get rid of that and that, and that, and that. And so now we have an account object and we're going to either withdrawal or deposit and then print out the balance. Let's see how this turns out. What kind of action do you wanna take? Let's say I want to deposit. How much do I want to put in? Let's say I want to put in $13. My balance is now 6350. That's correct. What kind of action do I want to take? What if I want to withdrawal? How much you want to take out? Let's say 13, 0.5c. So that gives us an even 50. And let's quit with Control C. Oh no, we get a keyboard interrupt error. And that comes from this action line. Let's try this. Try. Except. And what was that called? That was called a keyboard interrupt error. But nope, it was just called keyboard, keyboard interrupt. And let's copy that. There we go. And we're simply going to break out of this loop. Let's go ahead and try this out. What kind of action do I wanna take? Let's cancel. It. Simply counseled for us so we could even say print leaving the ATM. And let's put that on a new line. And put that on a new line. Due to, due to new, let's quit. It says Leaving the ATM and a gracefully left using a keyboard interrupt exception. Cool, this is coming together quite nicely. Now one last thing we need to consider is what if the action is not withdrawal or deposit? Let's do this. What kind of action do I want to take? Say I want to rob the bank? It's not going to do anything. This is a bad user experience. It just keeps asking, which I guess is not the worst user experience, but it's not a good user experience. So we can zip this up or collapse it. And we can say else. If the action is not withdrawal or deposit, what do we want to do? Print. That is not a valid action. Try again. Now let's do this. Let's say we want to rob and not a valid action. Try again, rabbit. Ok, what if we want to with draw? It's a hard word to type. With. Raul without a space at the end. How much do I want to take out? Let's say I wanna take out $900. My balance currently as minus $849.50. So now I'm getting into overdraft and if you wanted to, you could extend this to overdraft and loans and know all sorts of things. But that is essentially the entire project in a nutshell. And when I cancel, it gracefully cancels. It doesn't just error out. It says I'm leaving the atm. So let's recap what this looks like. We have a class called bank. When you open up an account, how much are we putting in there by default? $0, I put in $50.50 cents. To begin with. Everytime there's a transaction, we're going to open up the transactions dot TXT file. Let's go take a look at that. And it's showing me everything in here. That's great. That's a doing exactly what I want it to do. For every transaction, whether it's a withdrawal or deposit, we're going to take that file, we're going to write to it and whatever that transaction string is, so withdrew or deposit the amount. Tab, tab, tab balanced with the balance in there. Cool, simple transaction logger. Then we've got a withdrawal method. We're going to try to cast this as a float. If we cannot cast this as a float, it's going to say the amount is 0. And if the amount is greater than 0, it will basically, as long as it's not 0, we can take some sort of action. We're going to automatically change that balance from whatever it currently was. So I opened up with 50.50 and it's going to minus the amount that comes in here. And then we're going to log that amount and that withdrawal. Lastly, we have deposit and same thing. We're going to try to cast this amount as a float so that we can do numerical math on it. If we can't is going to say that amount is 0. If that amount is if that number is in fact a proper number, if that amount is a proper number, we can then change that balance to be balanced is equal to balance plus whatever amount. And then we said in our transaction logger, deposited the amount. Lastly, we create a new account and then we said walk up to the ATM. Essentially that's what this is saying here. While something is true, just keep asking, keep asking and asking and asking action. What kind of action do you want to take? If we said Control, if we cancelled, we get a keyboard interrupt error and we're simply going to leave, we're going to break out of this loop. Otherwise, if the action is in withdrawal or deposit, take action here. And if that action is withdrawal, we can say how much do you want to take out and then withdrawal that amount. Otherwise, if it's not withdrawal, we know it's going to be one of these statements, withdrawal or deposit. We know that this is withdrawal. So by logic of reduction, this has to be deposit. Then we say the amount is going to be input. How much do you want to put in by counts dot deposit to the amount which is going to work all of its banking internals. And then we can print out our balance account dot balance. Otherwise, if someone is trying to rob the bank or do something else with the bank that they really shouldn't be doing. We're going to print that is not a valid action. Try again. It's going to then execute that loop one more time and go over and over and over again until you leave the ATM. And that's really all there is to this particular project. And we're making use of a lot of different things we learned in Python 301. We're not necessarily using everything, we're not using a generator, we're not using a decorator. Although if you wanted to, you could figure out a way to add those in there. And if you wanted to, you could probably turn logged transaction into some sort of decorator to decorate withdrawal and deposit with them so that we don't have to explicitly write it in here. That would be pretty cool if you could do that. And what else do we talk about? Pip ends? We probably should have done this in a PIP environments, but you know, we're not installing any packages, so we didn't need a PIP environment for that and we just needed regular Python on our computers. And we didn't use a generator because we didn't actually need to use a generator. Generator would be really good if we, we're looping through like a million different customers in our bank and we needed to figure out which customer defined. So that's all there is to this particular banking app. If you didn't give this a try and you just watch right through this video. Please go ahead and try this out. Now, this is really, really important because it's great practice using a class. Don't forget if you have questions, you can always join the learning to code Group on Facebook. We're happy to answer any Python question that you have in there.
16. Python 301 summary: Welcome to the Python 301 summary. I have been your teacher. You're very proud teacher by the way, my name is Caleb Eataly and you can follow me at Kilo Tolkien on Twitter. Or if you want coding tips and tricks, you can always follow coding dot for dot everybody on Instagram. If you ever need any help, there is a free support group with tens of thousands of members in it called learning to code on Facebook. All you need is a Facebook account. It's absolutely free. We spend hours in their everyday grooming it to make sure that there's high-value posts in there and there's no spam whatsoever. So it's a good group to join. If you like. My teaching method, don't forget, I have several other courses. Just look me up. My name is Caleb, tall, lean, and you can find them on all sorts of platforms out there, including YouTube. Thank you for taking Python 301. And again, I have been your host and your instructor, Caleb Colleen, and I hope to be working with you in the future now that you are an advanced Python programmer, I hope you had fun and I'll see you around. Bye.