Write composable, reusable Python classes using Mixins
Tags: python, oop, django
Published: 2020-10-02 14:35:29

An important principle in programming is DRY - **D**on't **R**epeat **Y**ourself. It's considered good practice to avoid repeating similar chunks of code in multiple places, and instead define the functionality once - say, in a separate function or class. Then, wherever that functionality is required, you can simply invoke the function or create an instance of the class to do what you need.

Why is DRY important? Because, when you inevitably have to make changes in your code sometime in the future, you want to be able to make those changes at one place, rather than have to hunt through your entire codebase. Not only does this make your life as a developer a lot easier, it also reduces the risk of you missing out on one usage instance and thus creating inconsistent behaviour in your code.

One way to follow the DRY principle in Python is to make use of mixins. Let's see what mixins are and how they help us.

>💡 Note: this article assumes you have some understanding of basic OOP (Object Oriented Programming) concepts. If you are not very familiar with OOP yet, don't worry! I've found [this article](https://realpython.com/python3-object-oriented-programming/) to be very useful in explaining the basics.

# Inheritance in Python

On important thing to note is that Python, unlike some other Object-Oriented languages such as Java or C#, permits multiple inheritance. That is, a single class can have multiple parent classes. This is important because, as we'll see very soon, multiple inheritance is the basis for mixins in Python.

# What is a Mixin?

Let's say you're baking pizza for your family. The problem is, your entire family is very fussy when it comes to food. Your sister likes her pizza with olives, your father likes it with sausage, your mother likes neither and you like both!

How do you go about cooking 4 different pizzas efficiently? Would you make the toppings for each pizza from scratch, each time?

No! You'd probably first buy all the ingredients you need at once, then grate the cheese, chop up the olives, chop up the sausages and keep them aside in bowls. Then, as you're preparing each pizza, 'mix in' toppings whenever required before slipping it into the oven.

This example leads us to the definition of a mixin: a piece of standalone optional functionality that can be 'mixed in' whenever required. In our example, the toppings (olives and sausages) are both examples of mixins. They are optional - that is, the pizza would taste just fine without them - but their presence acts as a sort of enhancement. The same mixin can be added to multiple pizzas, or multiple mixins can be added to a single pizzas.

# How Do I Create a Mixin in Python?

There is no special syntax for creating mixins. Just write a regular Python class.

```python
class OlivesMixin:
    
    def add_olives(self):
        print("Adding olives!")
```

Just like any other class, a mixin can extend other classes, if required. It can also be instantiated by itself, but there is usually little reason to do this. Remember, mixins are meant to assist _other_ classes by providing them specific functionality, not exist in isolation.

Now that we have defined our mixin, all we need to do to benefit from it is to make another class inherit it:

```python
class SistersPizza(OlivesMixin, PlainPizza):
    
    def prepare_pizza(self):
        self.add_olives()
```

This way, we can make use of the `add_olives` method anywhere in our class, just as if it was defined inside the class. At the same time, we can take advantage of reusability and use it in any other class we want, just by adding the `OlivesMixin` mixin!

In its current form, our mixin doesn't provide much utility, because it only consists of a single method that can simply be extracted and written as a standalone function. The real advantage of mixin classes is that, while they cannot contain any internal state of their own, they can manipulate the internal state of the class they are 'mixed' into.

```python
class PlainPizza:

    def __init__(self):
        self.toppings = []


class OlivesMixin:
    
    def add_olives(self):
        print("Adding olives!")
        self.toppings += ['olives']


class SistersPizza(OlivesMixin, PlainPizza):

    def prepare_pizza(self):
        self.add_olives()
```

A single class can inherit and make use of multiple mixins. One crucial point to note is that the mixins must always go to the _left_ of the 'base' class, i.e the main parent class should always be the right-most in the list of superclasses.

```python
class MyPizza(OlivesMixin, SausagesMixin, PlainPizza):
    ... 
```

This is important because of Python's MRO (Method Resolution Order), the priority order in which Python searches classes for a method. You can read about the reasoning behind this [here](https://www.ianlewis.org/en/mixins-and-python).

# Let's Take a Practical Example

Our pizza example is well and good for the sake of introduction, but it's not the kind of code we'd ever need to write in the real world. Let's take a more practical example.

The [Django web framework](https://www.djangoproject.com/) makes extensive use of mixins, both in its internal code and the APIs it exposes.

### TemplateResponseMixin & ContextMixin

Generally, if you want to render a template in Django, you'd use the TemplateView class.


```python
class MyView(TemplateView):

    template_name = 'my_template.html'
```

However, this class only works with GET requests. What if you run into a situation where you have to render a template in response to a POST or PUT request? You can return an individual TemplateResponse in each method ...

```python
class MyView(View):

    def post(self, request, *args, **kwargs):
        return TemplateResponse(
            request=request,
            template='my_template.html'
        )

    def put(self, request, *args, **kwargs):
        return TemplateResponse(
            request=request,
            template='my_template.html'
        )
```

... but this causes some unnecessary duplication of code, as we are specifying the template name twice.

For this purpose we can use Django's TemplateResponseMixin, which provides the `render_to_response` method.

```python
class MyView(TemplateResponseMixin, View):

    template_name = 'my_template.html'

    def post(self, request, *args, **kwargs):
        return self.render_to_response(context={})

    def put(self, request, *args, **kwargs):
        return self.render_to_response(context={})
```

The `context` parameter above, which is currently empty, allows us to pass a dictionary of [context variables](https://docs.djangoproject.com/en/3.1/topics/templates/#variables), which are dynamically inserted into our template. For this purpose, it is also useful to subclass the ContextMixin, containing the `get_context_data` method.

```python
class MyView(TemplateResponseMixin, ContextMixin, View):

    template_name = 'my_template.html'

    def post(self, request, *args, **kwargs):
        return self.render_to_response(context=self.get_context_data())

    def put(self, request, *args, **kwargs):
        return self.render_to_response(context=self.get_context_data())

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # Add your context variables here
        return context
```

TemplateResponseMixin and ContextMixin are almost always found together, since they work well in tandem. You can read more about them [here](https://django.readthedocs.io/en/stable/ref/class-based-views/mixins-simple.html)

# Conclusion
We've seen how using Python mixins in our classes helps make our code more modular. The resulting code is shorter, simpler to understand and follows DRY best practices. Mixins are used everywhere in the object-oriented Python world, from web development to AI.

I hope you found this article useful. If you spot any errors or would like to offer any suggestions, please feel free to drop a comment below.

Image by <a href="https://pixabay.com/users/joshuemd-230533/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=329523">joshuemd</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=329523">Pixabay</a>

# References and Further Reading
[What is a mixin, and why are they useful?](https://stackoverflow.com/questions/533631/what-is-a-mixin-and-why-are-they-useful)
[Using Mixins With Class-Based Views](https://simpleisbetterthancomplex.com/tips/2016/09/27/django-tip-15-cbv-mixins.html)
[Multiple inheritance and mixin classes in Python]
(https://www.thedigitalcatonline.com/blog/2020/03/27/mixin-classes-in-python/)
[Djangocon: Demystifying mixins with Django](https://reinout.vanrees.org/weblog/2015/06/03/10-mixins.html)