Multiple Superclasses8:31 with Kenneth Love
We're not limited to a single parent class in Python. Let's see how to use this feature to make our code even more clean and clear.
You can use
class.__mro__ to look at your class's method resolution order (MRO) if you're curious. Or use the
inspect.getmro() function with your class to get the same information. This can be really handy if you're not sure what order the classes are being assembled.
When you're using multiple inheritance,
super() calls become really important. They let things like
__init__ travel all the way up the chain to make sure the class has all of the bits and pieces that it needs.
Classes are all about organization. 0:00 They're a solid reliable way to break up your monolithic scripts into smaller 0:02 chunks so they're easier to understand, easier to reuse, and easier to customize. 0:06 You've already seen how we can use super to reuse the code written in a superclass, 0:10 but that only gets us part of the way there. 0:14 If we continue with our current example, 0:17 our thief class, think of all the things we eventually have to have in there. 0:18 Thieves can pick pocket, hide in the shadows, throw a few nasty daggers, 0:22 and dodge a tax better than some strong but slow warrior. 0:25 We don't want to implement all of that on our thief class. 0:29 Especially when other classes like maybe an assassin or 0:31 a ninja would meet some of those abilities too. 0:34 Let's get back to workspaces and 0:37 look at extending several classes to make one awesome one. 0:38 Alright, things have changed a little bit so you'll want to be sure and 0:42 relaunch the work space if you're following along in workspaces. 0:46 Now, I've prepared a couple of classes and modules already. 0:49 Here, there's this attributes.py module and 0:53 you can see we're inside the RPG directory. 0:56 And inside here, we have an Agile attribute and 0:59 we have a Sneaky one as well. 1:02 We'll use both of these, but say we're making a gymnast, they might be agile but 1:04 they wouldn't necessarily be sneaky. 1:08 I've also refactored the Thief class out into its own module called 1:10 thieves.py, wait a minute. 1:14 Refactored, what kind of word is that? 1:16 Well, refactoring is a common programming concept that sometimes is as easy as 1:19 cutting and pasting some code from one place to another. 1:23 Sometimes it's more complicated than that. 1:27 Ultimately though, it's just the tech jargon word for rearranging code into 1:29 a more logical state through renaming things, moving code between files, or 1:33 deleting or combining code where too much optimization was done. 1:37 So our thief character is still pretty similar to how it was before. 1:42 Thief inherits from character, but you notice we're not setting sneaky or agile. 1:45 We're gonna handle those inside these attributes. 1:50 So let's go ahead and do that. 1:54 I want this to be a sneaky, agile character, and 1:56 of course it should still be a character. 2:00 So let's say, from attributes import agile and sneaky. 2:01 And now I need to use them in the definition of my class, 2:08 now the order that you add items into the inheritance chain of your class, 2:12 this list of classes here that your class extends, that order matters. 2:15 It matters because of things like super and how Python figures out which super 2:20 classes methods to call when two of them have methods with the same name. 2:24 This the called the method resolution order or MRO. 2:28 And it can cause a lot of headaches. 2:32 But Python has gotten better and better at making things just work for you. 2:33 Well, let's look at our sneaky and agile classes. 2:39 Now, they both have an init inside of them. 2:42 And in both of them, the __init__ calls super, and it passes on args and kwargs. 2:46 So, that's good. 2:52 And they don't have any other methods where the names conflict. 2:53 So, they should be fine to use in either order. 2:56 You know what? I'm kinda tired to thinking about this. 2:59 I'm just gonna use them as if they're modifiers. 3:01 I'm gonna make a sentence, right? 3:03 So Character that is Agile and Sneaky. 3:04 I'm also getting kind of tired of testing this down in the shell constantly, 3:08 like having to import and making things. 3:11 So lets make a new file that we will call play.py, and 3:13 inside of here we'll import our thief class from thieves import Thief, and 3:17 then we'll say that kenneth is equal to a thief, and we give it the name Kenneth. 3:22 And then let’s print kenneth.sneaky, 3:29 kenneth.agile, let’s just print those for right now. 3:35 That covers two of our special attributes, right? 3:40 Maybe we will do a print kenneth.hide when the light level is 8. 3:42 All right, I think that's pretty good, and you know what, 3:49 let's do, here, let's say sneaky=False. 3:53 All right, that covers a lot of our stuff, 3:55 now feel free to change these attributes, change the names, 3:59 all that kind of stuff, make this your own code, you don't have to copy me exactly. 4:03 All right, so now let's try Python play. 4:07 And we get false for sneaky, because we set sneaky to false. 4:12 We get true for 4:15 agile because we didn't change that, and I failed to hide because I'm not sneaky. 4:16 All right. 4:21 But let's think about design for a minute, right? 4:22 Sometimes if you run this code, depending on the version of Python that you're on, 4:26 this might fail. 4:30 You might get an error that there's no attribute agile or no attribute sneaky. 4:30 And even though we're using super on all of these, sometimes the keys and 4:35 values don't always get passed up to where they need to be because 4:41 the MRO falls down and Python fails to put things where they need to go. 4:46 So, okay, as part of that, 4:51 we could add a super into the init of our character class. 4:53 And then everything should work in all versions of python no matter where we are, 5:00 right, and no matter how the inheritance chain is set up. 5:05 But, usually you want something that's more concrete as a class, 5:08 a class where things just stop, right? 5:13 It's the concrete, and right now I would think that our Character class is 5:15 the class that we want that, and we kind of have designed that, because our 5:19 Character class doesn't have a super in it, since it doesn't super anything, 5:23 it's expecting to be the final class that gets initialized. 5:28 Well that's cool, we can do that. 5:31 If it wants to be the final class, let's make it the final class. 5:32 So we'll put Character out here, and then we'll, 5:36 over this way and delete Character off of there. 5:43 And that reads better too, right, our thief is an agile sneaky character. 5:48 Okay, so let's see if this is still working. 5:53 And now we've got an error, we didn't get this error before, but now we get one. 5:56 And this time it says that init is missing a required positional argument name. 6:01 Okay, now, I know that we provided a name value, right? 6:08 We provided name right here. 6:11 But, this is what I was talking about with the super calls, 6:13 from the time it goes to this super calls, it's a lot of this positional argument and 6:17 doesn't put name or this arg. 6:21 It doesn't put this arg as the first argument that provided. 6:24 So, we have to make another design decision. 6:28 Now, we could go into our attributes and we could change all of these and 6:31 we could put a name right here, and then we could put name and then continue on. 6:36 But that means that all of our attributes have to know how a character is created. 6:40 Well, that's fine, but that creates what you'll hear a lot of developers call 6:46 tightly coupled code, or a tightly coupled design. 6:50 We want to create code that's loosely coupled, so 6:53 that it becomes easier to mix and match things around. 6:56 So instead, let's go change how the name argument in character is defined. 6:59 And let's make it a keyword argument, right? 7:05 Because we provided a default value. 7:10 All we have to do is add the equals and the empty string. 7:12 But we wanna make sure that there's a name that's set. 7:15 So let's add a check and an exception. 7:18 So if not name, and then we're going to raise ValueError, 7:21 and we'll say, name is required. 7:27 And then let's try to create our thief right now. 7:33 And we get our ValueError, name is required. 7:38 So now when we create our thief, let's go right here and 7:40 let's say name is equal to Kenneth. 7:43 So now, when we explicitly set this parameter, and remember explicit is better 7:46 than implicit, our whole character works just like we expected it to. 7:50 So great work everyone. 7:55 You'll hear a lot of developers talk about whether something is loosely or 7:58 tightly coupled. 8:01 Usually they'll complain about something being tightly coupled and for good reason. 8:02 Code that relies heavily on other code can make it very 8:06 difficult to add in the functionality you want and need. 8:09 For instance, imagine you had code to send messages to users of your app. 8:12 If your message sending code always used a single mechanism for delivering those 8:16 messages, like email, it would be really hard to add in SMS or push notifications. 8:19 If the code is more loosely coupled though and just expected there to be a send 8:24 message method, you could swap out the backend much more easily. 8:27
You need to sign up for Treehouse in order to download course files.Sign up