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

Mixin Classes

00:00 One of the great things about having access to multiple inheritance is that we can utilize what are called mixins. A mixin is a class that provides methods to other classes, but it’s not considered a base class itself.

00:18 This special class is going to expose some methods that the derived class can utilize—methods that will essentially be mixed in to the derived class.

00:31 When it comes to mixins, forget about the is a relationship. We’re not creating a specialized version of the mixin—we’re just inheriting from it so we can utilize one or more of its methods.

00:46 Let’s say you want to convert objects of certain types in your application to a dictionary representation. We could provide a .to_dict() method in each class, but the implementation of this method—or the code inside of it—is going to be identical for each class. Instead, let’s create a mixin class that defines that method and then we’ll make some other classes inherit from the mixin so they gain access to the method.

01:20 This mixin class will be like a utility, and as such, I don’t think we should code it in any of our existing modules. Instead, I’ll create a new module called representations, which will house this class.

01:38 The mixin will be called AsDictionaryMixin.

01:43 It’s only going to expose one method called .to_dict(), which will scan for all of the instance attributes in the object it’s being called on, and then return a dictionary where the keys are the attribute names and the values are the attribute values.

02:04 This is going to map an attribute name to its value, but in a convenient dictionary that we can work with. In order to do this, we’re going to have to use some fancy dictionary comprehension.

02:19 I’ll have a link down in the video notes if you’d like to learn more about this, but if you’re not familiar, just understand that this code will turn our instance attributes into a dictionary of attribute names and their associated values.

02:37 In fact, this self.__dict__.items() is a method that every custom class inherits from the object superclass, which returns a __dict__.items object that basically contains all the instance attributes and their values.

02:57 This is good, but you might notice that the code in this method is utilizing two other methods: ._represent() and ._is_internal().

03:11 We have to implement those methods too. The ._represent() method will attempt to return the dictionary representation of the value passed in.

03:25 I’m calling it ._represent() to indicate that this is a private method that should be used only within this class, notably by the .to_dict() method above. This method will be passed on to the child classes of this mixin, but the underscore (_) tells other developers not to use it outside this mixin class. It’s just a helper method, if you will.

03:53 Again, in the interest of not having this course be five hours long, I’m not going to walk through this line by line. But basically, this method will return the proper format of the passed-in value appropriate for a dictionary. This might be a string, or if the current object has a .to_dict() method itself—because it inherited from the mixin too—then it’ll return the dictionary generated by its .to_dict() method.

04:26 The last private method we need is ._is_internal(),

04:33 which will just filter out any instance attributes that are marked as private. All right. That’s it for the mixin class. To clarify how this actually works, let me utilize it in two other classes. First, I’m going to move over to the Employee class. Before I can use the mixin anywhere, I need to import it at the top.

05:03 Then, I can make the Employee class inherit from AsDictionaryMixin.

05:10 Remember, even though this is an inheritance relationship, the whole is a relationship we normally use does not apply here. The Employee is not an AsDictionaryMixinit simply inherits the .to_dict() method for use with this Employee object.

05:31 That’s it. I don’t want every instance attribute to be represented in the generated dictionary, and so I’m going to mark two attributes as private by appending an underscore (_) at the beginning of their names. If you remember from before, the dictionary comprehension code filters out any instance attributes that are marked as private, so these two instance attributes will not appear in our generated dictionary.

06:01 The other class I want to inherit from AsDictionaryMixin is the Address, which represents an employee address. That’s something that would make sense being represented as a dictionary.

06:16 I’ll move over to contacts.py and make the Address class inherit from our mixin.

06:29 Now, we’re ready to test. There’s a few ways we can do this, but I think the easiest would be to move over to program.py and comment out most of the code we have.

06:42 I’m using Visual Studio Code, so I can highlight the code I want to comment out and press Command + K + C. Other IDEs have this capability too, although the keyboard shortcut may be different.

06:58 Commenting out this code will allow us to temporarily disable it. This demo will utilize a bit of json, so I’ll import that at the top. I have a video course on JSON if you’d like to learn more about it.

07:14 I’ll move down below the commented-out code and define a one-liner method called print_dict(), which will take a dictionary and print it in JSON format.

07:29 Now, all that’s left to do is iterate through all of our Employee objects and print each one’s dictionary representation.

07:40 This works because the Employee class inherits the .to_dict() method from the mixin class, so every Employee object has access to it.

07:53 And as you can see, there’s the "id", the "name", and the "address" of each employee, encoded as a dictionary.

08:02 The ._role and ._payroll attributes we marked as private, and so they were filtered out of the dictionary representation. Also, notice that the address of each employee is a dictionary in and of itself.

08:18 That’s because we made the Address class inherit from AsDictionaryMixin, so it too was able to represent itself as a dictionary.

08:29 As you can see, mixins are a handy way to utilize multiple inheritance. They allow for the same method to be reused in multiple classes, and because they only inherit methods and not attributes, we don’t run the risk of causing any problems.

Avatar image for dejiok

dejiok on April 19, 2020

Hi, thanks for this exellent video. Do you mind explaining this section of the code..

    def _represent(self, value):
        if isinstance(value, object):
            if hasattr(value, 'to_dict'):
                return value.to_dict()
            else:
                return str(value)
        else:
            return value
Avatar image for kbram

kbram on June 15, 2020

I am also interested in understanding this code. Can you respond with the link to explain the 19APR2020 comment?

Avatar image for Roy Telles

Roy Telles on Aug. 16, 2020

From what I gather by the video’s small comments and looking up the code by line (where needed) this is what I came up with:

def _represent(self, value):
        if isinstance(value, object):
            if hasattr(value, 'to_dict'):
                return value.to_dict()
            else:
                return str(value)
        else:
            return value
  • if isinstance(value, object): checks if the value passed is an object (i.e. a class)
  • if hasattr(value, 'to_dict'): if it is a class, check if it has a to_dict attribute (more on this later)
    • return value.to_dict(): if it does have a 'to_dict' attribute, call that class’s to_dict method
    • NOTE: In previous lessons, we learned that methods can be referred to as attributes when not passed any arguments, hence methods can be referred to as attributes and hence hasattr() is used to make this check
  • else: return str(value): if there is no to_dict attribute in the class, return the string representation of the passed in value instead
  • else: return value: if the passed in value isn’t an instance of the object super class, return the value itself

In general this method seems to be checking 3 things:

  1. If value is a class, check for a to_dict method, if there is, call that class’ to_dict method
  2. If value is a class, but DOESN’T have a to_dict method, return the string representation of the value passed in (noted by str(value))
  3. If value ISN’T a class, return the value itself

As far as isinstance and hasattr are concerned, these are the tests I ran in a REPL:

>>> string = 'hello'
>>> isinstance(string, object)
True
>>> dir(string)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> hasattr(string, 'isspace')
True
  • I passed in a string “value” into isinstance, checking to see that it’s an instance of the object super class (it is, as we know)
  • I checked to see what attributes the string class comes with by using dir(string) just to get a simple case example to use with hasattr
  • As an example I passed 'isspace' to hasattr to see what gets returned, and this seems to be a boolean that checks whether the value or class passed has the provided attribute

As far as why this check needs to be done in general, that’s beyond me, since as of Python 3 everything inherits from the object super class so it appears to me that isinstance(value, object) would always return true. But I was able to find this StackOverflow article about making your code “Python Agnostic” (adhering to both Python 2 and Python 3 syntax). Hope this helps :)

Avatar image for Saul

Saul on Nov. 11, 2020

To piggyback on Roy’s answer, it seems that in Python 3 this method could have been simplified and written as the following (given that any value that we could reasonably pass in should inherently be an object):

def _represent(self, value):
    if hasattr(value, 'to_dict'):
        return value.to_dict()
    else:
        return str(value)

Given this simplification, the next question you might have is: Why not just do the following instead?

def _represent(self, value):
    return str(value)

This would work but the output would look like the following:

{
  "id": "1",
  "name": "Mary Poppins",
  "address": "121 Admin Rd.\nConcord, NH, 03301"
}

It would be nice if the class allowed us to also reformat the address output so that the final result would like this instead:

 {
   "id": "1",
   "name": "Mary Poppins",
   "address": {
     "street": "121 Admin Rd.",
     "street2": "",
     "city": "Concord",
     "state": "NH",
     "zipcode": "03301"
   }
 }

Here we have a dictionary (address) printed within a dictionary. We can now see the keys in the key-value pairs for the address. You’ll notice that the id and name look just as before. So how do we achieve this? It involves a recursive call to to_dict(). Whenever we come across an object that inherits from AsDictionaryMixin, we can call its to_dict() method to print it in this specialized key-value dictionary format.

There are two classes that inherit from AsDictionaryMixin: Employee and Address. We initiate a call to_dict() on the Employee objects in our main script code found in program.py:

for employee in EmployeeDatabase().employees():
    print_dict(employee.to_dict())

Then as we print each employee object using to_dict(), we step through each element of that employee.

def to_dict(self):
    return {
        prop: self._represent(value)
        for prop, value in self.__dict__.items()
        if not self._is_internal(prop)
    }

First we find the id, and print it as a key-value pair. Next we find the name and print it similarly. Then we come across the embedded Address object. Address objects inherit from AsDictionaryMixin. As such, we can print them using the same to_dict() method. We recognize that an object inherits from AsDictionaryMixin by looking for the inherited attribute/method to_dict(). That’s where the conditional if hasattr(value, 'to_dict'): code comes into play. If the object we are printing inherits from AsDictionaryMixin (and thus has the to_dict() method), we opt to make the recursive call to to_dict() to give it the desired formatting:

def _represent(self, value):
    if hasattr(value, 'to_dict'):
        return value.to_dict()
    else:
        return str(value)

Otherwise, we just print the value in its standard string representation (like we see with the id or name).

It might be interesting to note that we could have changed the condition to look more directly for whether the object in question inherits from AsDictionaryMixin. That code would look like this:

def _represent(self, value):
    if issubclass(value.__class__, AsDictionaryMixin):
        return value.to_dict()
    else:
        return str(value)

This achieves the same effect.

Avatar image for Ariba S

Ariba S on June 10, 2024

Are mixins simply like composition relationships but without the .__init__() method?

Avatar image for Bartosz Zaczyński

Bartosz Zaczyński RP Team on June 10, 2024

@Ariba S They’re like Java interfaces with default method implementations. In other words, their only purpose is to augment concrete classes with additional behavior. Mixins can’t stand on their own, so there’s little point in instantiating them directly. Instead, they modify or hook into existing classes thanks to multiple inheritance.

Notable examples of mixins in Python include the ThreadingMixIn and lots of features in Django.

Become a Member to join the conversation.