Emulating Built-ins8:01 with Kenneth Love
What if we want our classes to act like a list or a dict?
One nice thing about emulating built-ins is that you can have your cake and eat it, too. You can make a class that's iterable but not searchable, or vice versa. This gives you a lot of control over how your classes are used.
yield lets you send data back out of a function without ending the execution of the function. Here's an example:
def get_numbers(): numbers = [4, 8, 15, 16, 23, 42] for number in numbers: yield number
If we used this function, with something like
numbers = get_numbers(), we'd have a generator object. This is a special kind of object that has a value, a pointer to the current index, and a
__next__ method (ooh, special method!) that knows how to get the next item from the iterable. We can do
next(numbers) and we'd get 4, then 8, then 15, and so on.
Since we're just returning values from an iterable, we can use
yield from to skip the entire
def get_numbers(): numbers = [4, 8, 15, 16, 23, 42] yield from numbers
If you want to read more about
yield from, here are some docs.
We've made a class that implements its own string, integer, and float conversion. 0:00 We've also made that class able to do addition. 0:04 Now let's see if we can make our own class that acts like a list. 0:06 Are these skills you'll need to survive in the Python world? 0:09 You'd be surprised how often you can cleanly solve a problem by making your own 0:12 iterable object. 0:15 Okay, let's get into workspaces. 0:17 So first, I think I wanna see about making my own list-like. 0:19 Now, I've already made a class and a module here, called items. 0:22 And this has a couple of different things in it. 0:26 We're gonna use these, probably, but we're not gonna be editing that. 0:28 I've also started this inventory.py with a class called Inventory, 0:33 and this is the thing I wanna make into a list-like. 0:36 So I think I do wanna have an inventory of stuff that belongs to a character. 0:40 And it hold their weapons, and their money, and 0:45 their magic potions, and whatever. 0:48 We have our class Inventory, let's start by adding some stuff to this, 0:51 so that we can put things into an Inventory. 0:54 So let's do like, def __init__(self), and 0:56 we're gonna say that our Inventory has slots, and that slots is a list. 1:01 And now let's do def add, and we're gonna add an item. 1:07 And let's do self.slots.append(item). 1:12 All right, so pretty simple, pretty basic. 1:17 I want to be able to check the length of someone's inventory. 1:20 I want to be able to like, you have 10 things in your inventory. 1:24 Or maybe I want to be like a lot of games and 1:26 only allow you to store 10 things or 15 things. 1:28 So to do that, if I'm in Python, I would use the len function, right? 1:32 And to use the len function on my object, 1:38 my object has to have a method that's named Duderlin, Dunder. 1:40 Have you noticed the pattern to this? 1:44 It's kind of how everything works? 1:48 So I'm gonna return len(self.slots), and self.slots is a list. 1:50 It already has the ability to get it's only length and 1:57 we're just gonna send that out and pretend that that's our length. 1:59 We're gonna be sneaky. 2:03 So let's try this. 2:04 Come down here to our shell, we'll go into our RPG directory, 2:09 go into Python and I'll say from inventory import Inventory. 2:14 And I'll say from items import item. 2:19 All right, so let's make a coin and I'll say coin = Item('coin'). 2:24 And I'll say this is a gold coin because we provide a name and 2:27 the description to each of the items. 2:31 And then my inventory will be a new inventory instance, and 2:34 then I'll say inventory.add(coin) and if I say len(inventory), 2:39 just in my instance, not inventory.slots, I get 1. 2:45 All right, so that's cool. 2:49 Now what about checking to see if an item is in the inventory? 2:52 I should be able to say, hey is this thing, 2:57 whatever the thing is, in the inventory? 3:00 And Python should tell me yes or no, or well, true or false. 3:02 And yeah, I could just check inventory.slots, but I shouldn't have to 3:06 know that there's an attribute named slots that actually holds everything, right? 3:10 I shouldn't have to know the inner workings of a class in order to use it. 3:14 Luckily, this is all self contained in a single magic method named contains. 3:17 So we'll go ahead and we'll define contains right down here. 3:23 And we take self, and 3:28 we take item, which is the thing that we're wanting to look for. 3:29 So we'll just return whether or not item is in self.slots. 3:33 Now, can it really be that simple? 3:39 What do you think? 3:41 Well, lets test this one more time. 3:43 From inventory import Inventory, from items import Item, 3:48 and sword = Item('sword', 'sharp'). 3:54 That's a good description, right? 3:58 And inventory = Inventory. 4:02 And then we'll say, inventory.add the sword, add the sword to my inventory. 4:06 Sword in inventory, true. 4:12 I guess it is that simple. 4:15 So contains actually does something kind of interesting if contains doesn't exist. 4:17 Python tries two other magic methods, it tries iter and it tries getitem. 4:23 These two methods aren't meant for checking membership but 4:30 they can be used for it. 4:33 If iter exists the object is considered to be iterable, and 4:35 Python can loop through it, so it will. 4:39 It will loop through it, trying to find something that is equivalent to the thing 4:41 that you have asked for. 4:45 If iter doesn't exist, Python tries to use getitem, which is the method that lets you 4:47 pluck things out of a collection with an index or a key, right? 4:52 When we've done like list bracket zero. 4:55 It checks a bunch of positive indexes in order and 4:58 tries to find one whose value matches the thing that you're checking for. 5:01 I think it'd be handy to be able to loop through an inventory, 5:06 now that we're talking about that. 5:08 So to do that, we'll need to create the iter method that I mentioned before. 5:10 But I need to warn you, we're gonna have to use a new keyword. 5:15 And its full explanation doesn't really fit into this course. 5:19 I have, of course included the documentation for 5:24 it in the teachers notes. 5:26 So check those out. 5:28 So, we're gonna say, for item in self.slots yield item. 5:29 What is this yield keyword? 5:36 It's very similar in how it works to return, 5:38 which you should be used to by now. 5:42 But it doesn't immediately end the execution of the method, like return does. 5:43 Yield lets us send values back out of the function, or 5:48 the method, as they're available, and keep on working. 5:51 This construct, a function or 5:55 a method that returns values as it works them out, is known as a generator. 5:57 We can actually simplify this by using two keywords, what we have written here. 6:01 We can say yield from item, or 6:06 not from item, from self.slots rather. 6:12 And that will do the same as the for loop we just had. 6:16 Since self.slots is a list, Python knows how to iterate through it. 6:20 Then, the yield from keyword combo sends out one item at a time 6:25 while Python is iterating through self.slots. 6:30 So, thanks for saving us some work, Python. 6:33 Let's test this out one last time. 6:36 Pop down here and I'll type python correctly. 6:41 And I'll say from inventory import Inventory. 6:46 And I'll say from items import weapon. 6:50 Let's try this with something slightly different. 6:52 This time we'll make our sword a weapon and we have to provide that it's a sword, 6:55 that it's sharp, and this is how much damage it does, so 50. 6:59 And then we'll say inventory = Inventory(), inventory.add(sword). 7:05 And then we'll say for item in inventory:, 7:12 print(item.description). 7:17 And we should get out sharp, nice. 7:21 A great thing about using a generator here is that even if our inventory is huge, and 7:24 I mean hundreds, thousands of items, iterating through it will 7:28 be memory efficient because we'll only be dealing with a single item at a time. 7:31 Wow, our class Is doing some pretty cool stuff. 7:36 When you make custom classes that can be used in the same fashion as built-in 7:38 classes you make it easier to use your code. 7:41 You don't have to think of any special cases like right, when I use a whiz bang I 7:43 have to call the configure method and then get all method and then. 7:47 No, just add things to it, sort it, get the length, etc. 7:50 just like you would any other object. 7:53 Okay, take a brain and body break then come back to learn about taking 7:55 control with Python's built in classes. 7:58
You need to sign up for Treehouse in order to download course files.Sign up