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

Unlock This Lesson

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

Unlock This Lesson

Hint: You can adjust the default video playback speed in your account settings.
Hint: You can set your subtitle preferences in your account settings.
Sorry! Looks like there’s an issue with video playback 🙁 This might be due to a temporary outage or because of a configuration issue with your browser. Please refer to our video player troubleshooting guide for assistance.

Interfaces

00:00 Before we can start modeling a real software project, we need to talk about one more thing: interfaces. These will show up in our UML diagrams and they’ll make it easier to visualize the relationships between many classes. Conceptually, an interface is a description of an object’s features and behaviors.

00:23 This equates to the set of attributes and methods that make up a class.

00:29 It’s important to note that this is a list of declarations and not implementations. What this means is that an interface will say that some class contains some methods, but it doesn’t specify how they’re implemented or the code that actually makes up those methods. It’s like saying, “All mammals can move.” It doesn’t matter how they move—whether they walk or fly—all they need to do to conform to the interface is to declare that they can move in some way.

01:04 Some languages such as C# and Java have an actual mechanism called an interface, which lists these class members. Python does not have or need this because unlike most other languages, Python supports multiple inheritance, which you will learn about later on. Instead, we’re going to utilize the conceptual idea of interfaces within our UML diagrams to better understand how classes relate to one another.

01:38 And, in case it wasn’t already clear, interfaces in software architecture are completely different than user interfaces that define the actual look of a piece of software. To solidify this idea, let’s see what classes on the right conform to, or implement, an interface.

01:59 The interface isn’t in the diagram here, but you can pretend that it lists two methods, .quack() and waddle(). In other words, some part of our program needs an object that conforms to that interface, having the ability to quack and waddle. It doesn’t matter how they do it, it just has to know that they can quack and waddle.

02:23 Take a look at this UML diagram on the right and see if you can figure out which classes conform to this interface.

02:33 It turns out we have three classes that implement this interface. Remember, for a class to implement an interface it must have the members—like the attributes and methods—described in the interface. The interface at hand lists .quack() and .waddle() methods, so any class on the right that has these two methods will conform.

02:59 So, what classes declare the ability to quack and waddle? Duck does because we can see that it lists .quack() and .waddle(). AttackDuck does too because it inherits those methods from its parent class.

03:15 Even if it overrides the implementation of one of the methods, it still conforms because the interface only needs the class to declare that the method exists.

03:27 It doesn’t care about the implementation or the code that makes up the method. We can see that the Swan also conforms, because it has .waddle() and .quack() methods.

03:39 Sure, it has other abilities, like .eat(), but that doesn’t matter. It still conforms. The Dog class does not conform to our interface.

03:50 That’s because it does not declare both of the required methods. This little inheritance relationship between Duck and AttackDuck is a great way to explain something called the Liskov substitution principle. Formally, this says that if S is a subtype, or a child class, of T,

04:13 then objects of type T may be replaced with objects of type S without altering any of the desired properties of the program. In other words, anywhere our program expects us to pass in a Duck object, we can pass in an AttackDuck object instead.

04:34 Why does this work? Because an AttackDuck is a Duck. It inherits the interface of the Duck, and so it’s guaranteed to have the same capabilities that our program is expecting—like, maybe the ability to quack and waddle.

04:53 We actually saw this in action before when we created our custom exception class. When we tried to raise it, Python told us that it needs to inherit from BaseException, so we made our class inherit from Exception, which is a class that itself inherits from BaseException.

05:14 This meant that our class inherited the interface of Exception, which already contained the interface for BaseException. Python expected a BaseException, but we gave it a custom exception type that conformed to the interface it was expecting, and so we were able to raise the exception with no problem.

05:37 This blew my mind when I first learned about it. As you’ll see later, it’s a good idea to try to follow this Liskov substitution principle when you can. It’ll save you from some inheritance headaches later on.

05:52 Let’s take a look at the interface of an actual Python class. This class declares two class attributes, .id and .name, and also a method called .calculate_payroll(), which returns an integer.

06:08 If an interface of some class lists its members, then an object instantiated from that class can be passed to any part of the program that’s expecting the same set of those members, or a subset of them.

06:26 Take a look at this interface, IPayrollCalculator. It’s common for interfaces to start with the letter I and end with either able or or—like IPayrollCalculator, IEnumerable, and IComparator.

06:44 This is not required, but it’s a good convention to follow when creating interfaces within your UML diagrams. This convention is really saying that a conforming class is able to do something—

06:59 like, in this case, have its payroll calculated. The interface lists three members that a class must declare to conform, and because those members are present in PayrollObject, that class conforms. By the Liskov substitution principle, anywhere our program is expecting a class that conforms to IPayrollCalculator,

07:26 we can pass in a PayrollObject.

07:30 As you might’ve noticed, interfaces look a little bit different from classes in UML diagrams. They’re usually a different color and they contain the word <<interface>> written between two angled brackets at the top.

07:46 It’s important to remember that an interface is not a class or any actual mechanism in Python. It’s just a listing of members that conforming classes must have.

08:00 When a class conforms to an interface, we draw a dashed line from the class to the interface, and write implements. To test your understanding of inheritance and interfaces, I’ve come up with a few questions to ask yourself.

08:19 Hopefully, these help to clarify any misunderstandings you might have. Pause this video and see if you can answer these questions for yourself.

08:31 The first question asks, “What is Employee called?” Here, Employee is the base class, or parent class, of SalaryEmployee and HourlyEmployee, directly.

08:46 As for what SalaryEmployee is called, that depends on the relation. In relation to Employee, SalaryEmployee is the child class, or the derived class. In relation to CommissionEmployee, it’s the parent class, or the base class.

09:06 The next question asks what interface Employee exposes. The interface for Employee lists two attributes, .id and .name. HourlyEmployee exposes a similar interface, except it includes the .calculate_payroll() method.

09:26 This class conforms to an interface listed in the diagram called IPayrollCalculator.

09:34 The other specialized employee classes conform to IPayrollCalculator too,

09:40 because they inherit the .id and .name attributes from the Employee class they derive from, then provide their own .calculate_payroll() methods.

09:52 Why might CommissionEmployee inherit from SalaryEmployee and not Employee directly? Well, according to this diagram, it looks like the .calculate_payroll() method in CommissionEmployee might utilize the .calculate_payroll() method in SalaryEmployee.

10:12 It will provide its own implementation of that method that will automatically hide the implementation of its parent, but it can use its parent as a part of its own implementation, as we will see later on. In the real world, this would mean that a commission employee probably has a salary too, and so we should extend SalaryEmployee instead of Employee.

10:39 Finally, we have “What is the difference between an interface and a class?” An interface is nothing more than a list of members. These members can be attributes or methods.

10:53 Any class that implements, or conforms to, this interface must declare these members. Interfaces aren’t built into Python like in other languages, but we can still use them conceptually as a part of our UML diagrams.

rafalcode on Jan. 11, 2022

I’d get rid of the “is called”, just “What is Employee?”. “Call” is used in its loose sense here, but in program it often has a very specific meaning.

muondude on Jan. 13, 2022

So the method name can be the same across related classes (parent, child) but the child method can use the base class method to implement its own unique method (with the same name) appropriate for the child class. Correct? I’m asking since I guess I hadn’t realized this until now. Interesting.

Bartosz Zaczyński RP Team on Jan. 14, 2022

@muondude Yes, that’s correct. Here’s a short example that demonstrates a child class overriding its parent’s method in a way you just described:

>>> class Parent:
...     def name(self):
...         return "Joe Doe"

>>> class Child(Parent):
...     def name(self):
...         return super().name() + " Jr."

>>> child = Child()
>>> child.name()
'Joe Doe Jr.'

On the one hand, the child delegates to the parent, but it also modifies the original behavior.

Become a Member to join the conversation.