Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Using Mixins With Abstract Base Classes

00:00 An abstract base class establishes a contract through listing a number of abstract methods that you must implement. But now you might wonder, can’t you leverage the abstract methods defined in the abstract base class to implement even more methods?

00:16 For example, for the class Sequence, if you already have .__getitem__() and .__len__(), doesn’t that allow you to iterate over the sequence?

00:25 Because you know how to get each element with a successive index and you know how many elements there are? And that’s precisely the train of thought that takes you to using mixins with abstract base classes. Go ahead and open the file sequence.py from the downloadable materials.

00:43 Take a look at the abstract base class Sequence that’s defined on the screen. The implementation of the abstract base class tells you that if you have the dunder methods .__getitem__() and .__len__(), then you have a sequence.

00:57 But this is enough for you to say that a sequence must be something you can iterate over because you can get successive elements with .__getitem__(), and you know precisely how many of those there are.

01:09 So you can leverage these two abstract methods without you knowing about their concrete implementation to implement a third method, which is precisely what you can do in the abstract base class.

01:22 You can say that the dunder method .__iter__() that takes no arguments can be a generator function that’s leveraging .__getitem__() and .__len__() to implement its behavior. So what you’re going to do is start at the index 0 and while the index is less than the length of self, you’re going to return the value of self that’s… Sorry, you’re not going to return, you’re going to yield the value that’s at the given position at self[index].

01:54 And this generator function leverages .__getitem__() and .__len__() to provide a concrete implementation that automatically makes sequences iterables.

02:05 In other words, if you define a class that is a sequence, it will automatically be an iterable. To see this in action, you can create a class, for example, MyRange that’s going to mimic the behavior of the built-in range() in a very simple and limited way. You can say that MyRange inherits from Sequence. And you’re going to define .__init__() to accept only the stop integer.

02:32 And you’re going to say that the attribute stop is going to be stop. And then you define .__len__() to be the method that returns self.stop. So for the sake of simplicity, assume that stop is greater than or equal to 0. So no negative numbers.

02:52 And now you can create an instance of MyRange by saying something like r = MyRange(10). If you open your terminal and try to run this file, you should get an error.

03:05 And you get an error because MyRange inherits from Sequence, which is an abstract base class, but it did not define the abstract method .__getitem__().

03:15 That’s why you get an error. You forgot to fulfill the contract. So you need to get back to your code, and you need to implement .__getitem__() as well.

03:23 Now, thankfully, in this particular instance, let’s assume that the argument index is going to be an integer, so you don’t have to do any validation. You just need to check the bounds.

03:35 So if the index is between 0 and the length, you can just use stop directly. Then you’re going to return the number itself. Otherwise, you raise an IndexError.

03:49 Now that you’ve defined .__getitem__(), you fulfill the full contract and you should be able to create your instance r. But not only that, you can automatically iterate over it just because you provided the methods .__getitem__() and .__len__(). So if you go through it and print all of the numbers, and now if you open your terminal and run the file, you don’t see the numbers 0 through 9 being printed on the screen because you forgot to increment the counter.

04:22 So in your loop, where you define .__iter__(), your generator function, you need to increment index after each iteration, otherwise you get an infinite loop, which is what you just saw on the screen.

04:35 So if you fix your .__iter__() or if you implemented it correctly on the first try, now you can open your terminal, you can run python sequence.py, and you see the integers 0 through 9 being printed on the screen.

04:51 Again, if you go back to your code, note how the class MyRange does not define iteration explicitly. You got it for free because you fulfilled the contract of the abstract base class Sequence.

05:05 And this is where mixin methods play nicely with abstract base classes. When your abstract base class defines a couple of methods, you can get what’s typically referred to as a mixin method for free.

05:18 In this case, you get .__iter__() for free. So you can say that you get the mixin method .__iter__() because of the abstract base class.

05:27 In fact, if you open the documentation on collections.abc, you will find a table that shows precisely this. It lists the abstract base classes available in collections.abc, and closer to the bottom of the screen, you can find Sequence. The third column shows the abstract methods that Sequence defines, .__getitem__() and .__len__(), and the final column shows the mixin methods, the methods that you can get for free if you implement the abstract methods.

05:58 For the sake of clarity, note that what you did with the abstract base class Sequence, where you defined .__getitem__() and .__len__() as abstract methods, and then wrote .__iter__() in terms of .__getitem__() and .__len__(), that’s more or less what Python does behind the scenes.

06:20 In general, if you write a class that implements .__getitem__(), regardless of whether you inherit from your abstract base class Sequence or not, if your class defines .__getitem__(), you get iterability for free.

06:36 That’s what Python does behind the scenes. So the class Sequence you implemented was to show you more or less how Python can do this behind the scenes. Through a series of methods that you implement, you get others for free.

06:49 And this is the typical way in which abstract base classes are used in conjunction with mixin classes. As a quick challenge, see if you can implement a couple of the other mixin methods for Sequence, and when you’re happy with your code, move on to the next lesson, where you’ll learn why these methods you’re getting for free are called mixin methods.

Become a Member to join the conversation.