1 00:00:00,370 --> 00:00:02,700 Classes are all about organization. 2 00:00:02,700 --> 00:00:06,280 They're a solid reliable way to break up your monolithic scripts into smaller 3 00:00:06,280 --> 00:00:10,410 chunks so they're easier to understand, easier to reuse, and easier to customize. 4 00:00:10,410 --> 00:00:14,700 You've already seen how we can use super to reuse the code written in a superclass, 5 00:00:14,700 --> 00:00:17,380 but that only gets us part of the way there. 6 00:00:17,380 --> 00:00:18,990 If we continue with our current example, 7 00:00:18,990 --> 00:00:22,860 our Thief class, think of all the things we eventually have to have in there. 8 00:00:22,860 --> 00:00:25,870 Thieves can pick pocket, hide in the shadows, throw a few nasty daggers, 9 00:00:25,870 --> 00:00:29,050 and dodge attacks better than some strong but slow warrior. 10 00:00:29,050 --> 00:00:31,790 We don't want to implement all of that on our Thief class. 11 00:00:31,790 --> 00:00:34,080 Especially when other classes like maybe an assassin or 12 00:00:34,080 --> 00:00:37,000 a ninja would meet some of those abilities too. 13 00:00:37,000 --> 00:00:38,420 Let's get back to workspaces and 14 00:00:38,420 --> 00:00:41,150 look at extending several classes to make one awesome one. 15 00:00:42,870 --> 00:00:46,010 Alright, things have changed a little bit so you'll want to be sure and 16 00:00:46,010 --> 00:00:49,600 relaunch the work space if you're following along in workspaces. 17 00:00:49,600 --> 00:00:53,670 Now, I've prepared a couple of classes and modules already. 18 00:00:53,670 --> 00:00:56,430 Here, there's this attributes.py module and 19 00:00:56,430 --> 00:00:58,460 you can see we're inside the RPG directory. 20 00:00:59,620 --> 00:01:02,800 And inside here, we have an Agile attribute and 21 00:01:02,800 --> 00:01:04,510 we have a Sneaky one as well. 22 00:01:04,510 --> 00:01:08,370 We'll use both of these, but say we're making a gymnast, they might be agile but 23 00:01:08,370 --> 00:01:10,970 they wouldn't necessarily be sneaky. 24 00:01:10,970 --> 00:01:14,495 I've also refactored the Thief class out into its own module called 25 00:01:14,495 --> 00:01:16,950 thieves.py, wait a minute. 26 00:01:16,950 --> 00:01:19,290 Refactored, what kind of word is that? 27 00:01:19,290 --> 00:01:23,820 Well, refactoring is a common programming concept that sometimes is as easy as 28 00:01:23,820 --> 00:01:27,260 cutting and pasting some code from one place to another. 29 00:01:27,260 --> 00:01:29,485 Sometimes it's more complicated than that. 30 00:01:29,485 --> 00:01:33,365 Ultimately though, it's just the tech jargon word for rearranging code into 31 00:01:33,365 --> 00:01:37,885 a more logical state through renaming things, moving code between files, or 32 00:01:37,885 --> 00:01:42,265 deleting or combining code where too much optimization was done. 33 00:01:42,265 --> 00:01:45,955 So our thief character is still pretty similar to how it was before. 34 00:01:45,955 --> 00:01:50,720 Thief inherits from Character, but you notice we're not setting sneaky or agile. 35 00:01:50,720 --> 00:01:53,380 We're gonna handle those inside these attributes. 36 00:01:54,850 --> 00:01:56,310 So let's go ahead and do that. 37 00:01:56,310 --> 00:02:00,330 I want this to be a sneaky, agile character, and 38 00:02:00,330 --> 00:02:01,960 of course it should still be a character. 39 00:02:01,960 --> 00:02:08,870 So let's say, from attributes import Agile and Sneaky. 40 00:02:08,870 --> 00:02:12,220 And now I need to use them in the definition of my class, 41 00:02:12,220 --> 00:02:15,500 now the order that you add items into the inheritance chain of your class, 42 00:02:15,500 --> 00:02:20,940 this list of classes here that your class extends, that order matters. 43 00:02:20,940 --> 00:02:24,450 It matters because of things like super and how Python figures out which super 44 00:02:24,450 --> 00:02:28,800 classes methods to call when two of them have methods with the same name. 45 00:02:28,800 --> 00:02:32,410 This the called the method resolution order or MRO. 46 00:02:32,410 --> 00:02:33,950 And it can cause a lot of headaches. 47 00:02:33,950 --> 00:02:38,800 But Python has gotten better and better at making things just work for you. 48 00:02:39,930 --> 00:02:42,980 Well, let's look at our sneaky and agile classes. 49 00:02:42,980 --> 00:02:46,680 Now, they both have an init inside of them. 50 00:02:46,680 --> 00:02:52,080 And in both of them, the __init__ calls super, and it passes on args and kwargs. 51 00:02:52,080 --> 00:02:53,290 So, that's good. 52 00:02:53,290 --> 00:02:56,640 And they don't have any other methods where the names conflict. 53 00:02:56,640 --> 00:02:59,640 So, they should be fine to use in either order. 54 00:02:59,640 --> 00:03:01,830 You know what? I'm kinda tired to thinking about this. 55 00:03:01,830 --> 00:03:03,460 I'm just gonna use them as if they're modifiers. 56 00:03:03,460 --> 00:03:04,670 I'm gonna make a sentence, right? 57 00:03:04,670 --> 00:03:08,490 So Character that is Agile and Sneaky. 58 00:03:08,490 --> 00:03:11,740 I'm also getting kind of tired of testing this down in the shell constantly, 59 00:03:11,740 --> 00:03:13,499 like having to import and making things. 60 00:03:13,499 --> 00:03:17,175 So let's make a new file that we will call play.py, and 61 00:03:17,175 --> 00:03:22,401 inside of here we'll import our Thief class from thieves import Thief, and 62 00:03:22,401 --> 00:03:27,970 then we'll say that kenneth is equal to a Thief(), and we give it the name Kenneth. 63 00:03:29,610 --> 00:03:35,065 And then let’s print kenneth.sneaky, 64 00:03:35,065 --> 00:03:40,150 kenneth.agile, let’s just print those for right now. 65 00:03:40,150 --> 00:03:42,630 That covers two of our special attributes, right? 66 00:03:42,630 --> 00:03:47,910 Maybe we will do a print kenneth.hide when the light level is 8. 67 00:03:49,723 --> 00:03:53,147 All right, I think that's pretty good, and you know what, 68 00:03:53,147 --> 00:03:55,870 let's do, here, let's say sneaky=False. 69 00:03:55,870 --> 00:03:59,610 All right, that covers a lot of our stuff, 70 00:03:59,610 --> 00:04:03,560 now feel free to change these attributes, change the names, 71 00:04:03,560 --> 00:04:07,940 all that kind of stuff, make this your own code, you don't have to copy me exactly. 72 00:04:07,940 --> 00:04:12,560 All right, so now let's try Python play. 73 00:04:12,560 --> 00:04:15,680 And we get False for sneaky, because we set sneaky to False. 74 00:04:15,680 --> 00:04:16,250 We get True for 75 00:04:16,250 --> 00:04:21,320 agile because we didn't change that, and I failed to hide because I'm not sneaky. 76 00:04:21,320 --> 00:04:22,080 All right. 77 00:04:22,080 --> 00:04:25,350 But let's think about design for a minute, right? 78 00:04:26,940 --> 00:04:30,248 Sometimes if you run this code, depending on the version of Python that you're on, 79 00:04:30,248 --> 00:04:30,955 this might fail. 80 00:04:30,955 --> 00:04:35,980 You might get an error that there's no attribute Agile or no attribute Sneaky. 81 00:04:35,980 --> 00:04:41,343 And even though we're using super on all of these, sometimes the keys and 82 00:04:41,343 --> 00:04:46,101 values don't always get passed up to where they need to be because 83 00:04:46,101 --> 00:04:51,230 the MRO falls down and Python fails to put things where they need to go. 84 00:04:51,230 --> 00:04:53,978 So, okay, as part of that, 85 00:04:53,978 --> 00:04:59,490 we could add a super into the init of our character class. 86 00:05:00,540 --> 00:05:05,570 And then everything should work in all versions of python no matter where we are, 87 00:05:05,570 --> 00:05:08,510 right, and no matter how the inheritance chain is set up. 88 00:05:08,510 --> 00:05:13,030 But, usually you want something that's more concrete as a class, 89 00:05:13,030 --> 00:05:15,310 a class where things just stop, right? 90 00:05:15,310 --> 00:05:19,850 It's the concrete, and right now I would think that our Character class is 91 00:05:19,850 --> 00:05:23,590 the class that we want that, and we kind of have designed that, because our 92 00:05:23,590 --> 00:05:28,110 Character class doesn't have a super in it, since it doesn't super anything, 93 00:05:28,110 --> 00:05:31,480 it's expecting to be the final class that gets initialized. 94 00:05:31,480 --> 00:05:32,910 Well that's cool, we can do that. 95 00:05:32,910 --> 00:05:36,190 If it wants to be the final class, let's make it the final class. 96 00:05:36,190 --> 00:05:41,880 So we'll put Character out here, and then we'll, 97 00:05:43,640 --> 00:05:48,740 over this way and delete Character off of there. 98 00:05:48,740 --> 00:05:53,190 And that reads better too, right, our thief is an agile sneaky character. 99 00:05:53,190 --> 00:05:54,690 Okay, so let's see if this is still working. 100 00:05:56,910 --> 00:06:01,770 And now we've got an error, we didn't get this error before, but now we get one. 101 00:06:01,770 --> 00:06:08,421 And this time it says that init is missing a required positional argument name. 102 00:06:08,421 --> 00:06:11,630 Okay, now, I know that we provided a name value, right? 103 00:06:11,630 --> 00:06:13,860 We provided name right here. 104 00:06:13,860 --> 00:06:17,196 But, this is what I was talking about with the super calls, 105 00:06:17,196 --> 00:06:21,933 from the time it goes to this super calls, it's a lot of this positional argument and 106 00:06:21,933 --> 00:06:24,290 doesn't put name or this arg. 107 00:06:24,290 --> 00:06:28,530 It doesn't put this arg as the first argument that provided. 108 00:06:28,530 --> 00:06:31,530 So, we have to make another design decision. 109 00:06:31,530 --> 00:06:36,006 Now, we could go into our attributes and we could change all of these and 110 00:06:36,006 --> 00:06:40,938 we could put a name right here, and then we could put name and then continue on. 111 00:06:40,938 --> 00:06:46,776 But that means that all of our attributes have to know how a character is created. 112 00:06:46,776 --> 00:06:50,767 Well, that's fine, but that creates what you'll hear a lot of developers call 113 00:06:50,767 --> 00:06:53,940 tightly coupled code, or a tightly coupled design. 114 00:06:53,940 --> 00:06:56,240 We want to create code that's loosely coupled, so 115 00:06:56,240 --> 00:06:59,460 that it becomes easier to mix and match things around. 116 00:06:59,460 --> 00:07:05,350 So instead, let's go change how the name argument in Character is defined. 117 00:07:05,350 --> 00:07:10,230 And let's make it a keyword argument, right? 118 00:07:10,230 --> 00:07:12,150 Because we provided a default value. 119 00:07:12,150 --> 00:07:15,490 All we have to do is add the equals and the empty string. 120 00:07:15,490 --> 00:07:18,790 But we wanna make sure that there's a name that's set. 121 00:07:18,790 --> 00:07:21,750 So let's add a check and an exception. 122 00:07:21,750 --> 00:07:27,585 So if not name, and then we're going to raise ValueError, 123 00:07:27,585 --> 00:07:31,094 and we'll say, name is required. 124 00:07:33,170 --> 00:07:36,100 And then let's try to create our Thief right now. 125 00:07:38,510 --> 00:07:40,430 And we get our ValueError, name is required. 126 00:07:40,430 --> 00:07:43,500 So now when we create our thief, let's go right here and 127 00:07:43,500 --> 00:07:44,900 let's say name is equal to Kenneth. 128 00:07:46,010 --> 00:07:50,640 So now, when we explicitly set this parameter, and remember explicit is better 129 00:07:50,640 --> 00:07:55,960 than implicit, our whole character works just like we expected it to. 130 00:07:55,960 --> 00:07:57,200 So great work everyone. 131 00:07:58,520 --> 00:08:01,540 You'll hear a lot of developers talk about whether something is loosely or 132 00:08:01,540 --> 00:08:02,790 tightly coupled. 133 00:08:02,790 --> 00:08:06,650 Usually they'll complain about something being tightly coupled and for good reason. 134 00:08:06,650 --> 00:08:09,260 Code that relies heavily on other code can make it very 135 00:08:09,260 --> 00:08:12,390 difficult to add in the functionality you want and need. 136 00:08:12,390 --> 00:08:16,050 For instance, imagine you had code to send messages to users of your app. 137 00:08:16,050 --> 00:08:19,680 If your message sending code always used a single mechanism for delivering those 138 00:08:19,680 --> 00:08:24,610 messages, like email, it would be really hard to add in SMS or push notifications. 139 00:08:24,610 --> 00:08:27,750 If the code is more loosely coupled though and just expected there to be a send 140 00:08:27,750 --> 00:08:31,060 message method, you could swap out the backend much more easily.