1 00:00:00,120 --> 00:00:04,140 We've made a class that implements its own string, integer, and float conversion. 2 00:00:04,140 --> 00:00:06,670 We've also made that class able to do addition. 3 00:00:06,670 --> 00:00:09,980 Now let's see if we can make our own class that acts like a list. 4 00:00:09,980 --> 00:00:12,720 Are these skills you'll need to survive in the Python world? 5 00:00:12,720 --> 00:00:15,750 You'd be surprised how often you can cleanly solve a problem by making your own 6 00:00:15,750 --> 00:00:17,200 iterable object. 7 00:00:17,200 --> 00:00:18,380 Okay, let's get into workspaces. 8 00:00:19,690 --> 00:00:22,780 So first, I think I wanna see about making my own list-like. 9 00:00:22,780 --> 00:00:26,900 Now, I've already made a class and a module here, called items. 10 00:00:26,900 --> 00:00:28,910 And this has a couple of different things in it. 11 00:00:28,910 --> 00:00:33,040 We're gonna use these, probably, but we're not gonna be editing that. 12 00:00:33,040 --> 00:00:36,490 I've also started this inventory.py with a class called Inventory, 13 00:00:36,490 --> 00:00:40,280 and this is the thing I wanna make into a list-like. 14 00:00:40,280 --> 00:00:45,860 So I think I do wanna have an inventory of stuff that belongs to a character. 15 00:00:45,860 --> 00:00:48,090 And it hold their weapons, and their money, and 16 00:00:48,090 --> 00:00:51,520 their magic potions, and whatever. 17 00:00:51,520 --> 00:00:54,340 We have our class Inventory, let's start by adding some stuff to this, 18 00:00:54,340 --> 00:00:56,830 so that we can put things into an Inventory. 19 00:00:56,830 --> 00:01:01,950 So let's do like, def __init__(self), and 20 00:01:01,950 --> 00:01:06,750 we're gonna say that our Inventory has slots, and that slots is a list. 21 00:01:07,840 --> 00:01:12,060 And now let's do def add, and we're gonna add an item. 22 00:01:12,060 --> 00:01:17,430 And let's do self.slots.append(item). 23 00:01:17,430 --> 00:01:20,850 All right, so pretty simple, pretty basic. 24 00:01:20,850 --> 00:01:24,190 I want to be able to check the length of someone's inventory. 25 00:01:24,190 --> 00:01:26,685 I want to be able to like, you have 10 things in your inventory. 26 00:01:26,685 --> 00:01:28,720 Or maybe I want to be like a lot of games and 27 00:01:28,720 --> 00:01:32,680 only allow you to store 10 things or 15 things. 28 00:01:32,680 --> 00:01:38,220 So to do that, if I'm in Python, I would use the len function, right? 29 00:01:38,220 --> 00:01:40,950 And to use the len function on my object, 30 00:01:40,950 --> 00:01:44,792 my object has to have a method that's named __len__. 31 00:01:44,792 --> 00:01:48,130 Have you noticed the pattern to this? 32 00:01:48,130 --> 00:01:50,830 It's kind of how everything works? 33 00:01:50,830 --> 00:01:57,630 So I'm gonna return len(self.slots), and self.slots is a list. 34 00:01:57,630 --> 00:01:59,790 It already has the ability to get its only length and 35 00:01:59,790 --> 00:02:03,370 we're just gonna send that out and pretend that that's our length. 36 00:02:03,370 --> 00:02:04,930 We're gonna be sneaky. 37 00:02:04,930 --> 00:02:07,750 So let's try this. 38 00:02:09,190 --> 00:02:14,346 Come down here to our shell, we'll go into our RPG directory, 39 00:02:14,346 --> 00:02:19,521 go into Python and I'll say from inventory import Inventory. 40 00:02:19,521 --> 00:02:23,325 And I'll say from items import Item. 41 00:02:24,325 --> 00:02:27,985 All right, so let's make a coin and I'll say coin = Item('coin'). 42 00:02:27,985 --> 00:02:31,137 And I'll say this is a gold coin because we provide a name and 43 00:02:31,137 --> 00:02:33,165 the description to each of the items. 44 00:02:34,490 --> 00:02:39,225 And then my inventory will be a new inventory instance, and 45 00:02:39,225 --> 00:02:45,006 then I'll say inventory.add(coin) and if I say len(inventory), 46 00:02:45,006 --> 00:02:49,480 just in my instance, not inventory.slots, I get 1. 47 00:02:49,480 --> 00:02:52,080 All right, so that's cool. 48 00:02:52,080 --> 00:02:57,200 Now what about checking to see if an item is in the inventory? 49 00:02:57,200 --> 00:03:00,150 I should be able to say, hey is this thing, 50 00:03:00,150 --> 00:03:02,320 whatever the thing is, in the inventory? 51 00:03:02,320 --> 00:03:06,155 And Python should tell me yes or no, or well, True or False. 52 00:03:06,155 --> 00:03:10,615 And yeah, I could just check inventory.slots, but I shouldn't have to 53 00:03:10,615 --> 00:03:14,015 know that there's an attribute named slots that actually holds everything, right? 54 00:03:14,015 --> 00:03:17,505 I shouldn't have to know the inner workings of a class in order to use it. 55 00:03:17,505 --> 00:03:21,945 Luckily, this is all self contained in a single magic method named contains. 56 00:03:23,605 --> 00:03:27,015 So we'll go ahead and we'll define contains right down here. 57 00:03:28,940 --> 00:03:29,768 And we take self, and 58 00:03:29,768 --> 00:03:33,250 we take item, which is the thing that we're wanting to look for. 59 00:03:33,250 --> 00:03:39,041 So we'll just return whether or not item is in self.slots. 60 00:03:39,041 --> 00:03:41,940 Now, can it really be that simple? 61 00:03:41,940 --> 00:03:43,320 What do you think? 62 00:03:43,320 --> 00:03:46,049 Well, let's test this one more time. 63 00:03:48,724 --> 00:03:54,283 From inventory import Inventory, from items import Item, 64 00:03:54,283 --> 00:03:58,646 and sword = Item('sword', 'sharp'). 65 00:03:58,646 --> 00:04:02,157 That's a good description, right? 66 00:04:02,157 --> 00:04:05,352 And inventory = Inventory(). 67 00:04:06,600 --> 00:04:12,590 And then we'll say, inventory.add the sword, add the sword to my inventory. 68 00:04:12,590 --> 00:04:15,480 Sword in inventory, True. 69 00:04:15,480 --> 00:04:17,710 I guess it is that simple. 70 00:04:17,710 --> 00:04:23,650 So contains actually does something kind of interesting if contains doesn't exist. 71 00:04:23,650 --> 00:04:29,420 Python tries two other magic methods, it tries iter and it tries getitem. 72 00:04:30,580 --> 00:04:33,850 These two methods aren't meant for checking membership but 73 00:04:33,850 --> 00:04:35,980 they can be used for it. 74 00:04:35,980 --> 00:04:39,144 If iter exists the object is considered to be iterable, and 75 00:04:39,144 --> 00:04:41,313 Python can loop through it, so it will. 76 00:04:41,313 --> 00:04:45,850 It will loop through it, trying to find something that is equivalent to the thing 77 00:04:45,850 --> 00:04:47,262 that you have asked for. 78 00:04:47,262 --> 00:04:52,259 If iter doesn't exist, Python tries to use getitem, which is the method that lets you 79 00:04:52,259 --> 00:04:55,900 pluck things out of a collection with an index or a key, right? 80 00:04:55,900 --> 00:04:58,750 When we've done like list bracket zero. 81 00:04:58,750 --> 00:05:01,760 It checks a bunch of positive indexes in order and 82 00:05:01,760 --> 00:05:05,220 tries to find one whose value matches the thing that you're checking for. 83 00:05:06,580 --> 00:05:08,670 I think it'd be handy to be able to loop through an inventory, 84 00:05:08,670 --> 00:05:10,270 now that we're talking about that. 85 00:05:10,270 --> 00:05:14,010 So to do that, we'll need to create the iter method that I mentioned before. 86 00:05:15,240 --> 00:05:19,860 But I need to warn you, we're gonna have to use a new keyword. 87 00:05:19,860 --> 00:05:24,510 And its full explanation doesn't really fit into this course. 88 00:05:24,510 --> 00:05:26,900 I have, of course included the documentation for 89 00:05:26,900 --> 00:05:28,040 it in the teacher's notes. 90 00:05:28,040 --> 00:05:29,250 So check those out. 91 00:05:29,250 --> 00:05:36,260 So, we're gonna say, for item in self.slots yield item. 92 00:05:36,260 --> 00:05:38,780 What is this yield keyword? 93 00:05:38,780 --> 00:05:42,010 It's very similar in how it works to return, 94 00:05:42,010 --> 00:05:43,990 which you should be used to by now. 95 00:05:43,990 --> 00:05:48,790 But it doesn't immediately end the execution of the method, like return does. 96 00:05:48,790 --> 00:05:51,690 yield lets us send values back out of the function, or 97 00:05:51,690 --> 00:05:55,670 the method, as they're available, and keep on working. 98 00:05:55,670 --> 00:05:57,430 This construct, a function or 99 00:05:57,430 --> 00:06:01,930 a method that returns values as it works them out, is known as a generator. 100 00:06:01,930 --> 00:06:06,890 We can actually simplify this by using two keywords, what we have written here. 101 00:06:06,890 --> 00:06:12,320 We can say yield from item, or 102 00:06:12,320 --> 00:06:15,730 not from item, from self.slots rather. 103 00:06:16,950 --> 00:06:20,660 And that will do the same as the for loop we just had. 104 00:06:20,660 --> 00:06:25,180 Since self.slots is a list, Python knows how to iterate through it. 105 00:06:25,180 --> 00:06:30,410 Then, the yield from keyword combo sends out one item at a time 106 00:06:30,410 --> 00:06:33,940 while Python is iterating through self.slots. 107 00:06:33,940 --> 00:06:35,710 So, thanks for saving us some work, Python. 108 00:06:36,780 --> 00:06:39,012 Let's test this out one last time. 109 00:06:41,896 --> 00:06:44,200 Pop down here and I'll type python correctly. 110 00:06:46,030 --> 00:06:48,980 And I'll say from inventory import Inventory. 111 00:06:50,460 --> 00:06:52,730 And I'll say from items import Weapon. 112 00:06:52,730 --> 00:06:55,050 Let's try this with something slightly different. 113 00:06:55,050 --> 00:06:59,560 This time we'll make our sword a weapon and we have to provide that it's a sword, 114 00:06:59,560 --> 00:07:05,030 that it's sharp, and this is how much damage it does, so 50. 115 00:07:05,030 --> 00:07:12,340 And then we'll say inventory = Inventory(), inventory.add(sword). 116 00:07:12,340 --> 00:07:15,480 And then we'll say for item in inventory:, 117 00:07:17,984 --> 00:07:21,800 print(item.description). 118 00:07:21,800 --> 00:07:24,670 And we should get out sharp, nice. 119 00:07:24,670 --> 00:07:28,400 A great thing about using a generator here is that even if our inventory is huge, and 120 00:07:28,400 --> 00:07:31,620 I mean hundreds, thousands of items, iterating through it will 121 00:07:31,620 --> 00:07:35,420 be memory efficient because we'll only be dealing with a single item at a time. 122 00:07:36,540 --> 00:07:38,860 Wow, our class is doing some pretty cool stuff. 123 00:07:38,860 --> 00:07:41,980 When you make custom classes that can be used in the same fashion as built-in 124 00:07:41,980 --> 00:07:43,900 classes you make it easier to use your code. 125 00:07:43,900 --> 00:07:47,710 You don't have to think of any special cases like right, when I use a whiz bang I 126 00:07:47,710 --> 00:07:50,460 have to call the configure method and then get all method and then. 127 00:07:50,460 --> 00:07:53,720 No, just add things to it, sort it, get the length, etc. 128 00:07:53,720 --> 00:07:55,820 just like you would any other object. 129 00:07:55,820 --> 00:07:58,920 Okay, take a brain and body break then come back to learn about taking 130 00:07:58,920 --> 00:08:00,853 control with Python's built in classes.