Movatterモバイル変換


[0]ホーム

URL:


— FREE Email Series —

🐍 Python Tricks 💌

Python Tricks Dictionary Merge

🔒 No spam. Unsubscribe any time.

Browse TopicsGuided Learning Paths
Basics Intermediate Advanced
apibest-practicescareercommunitydatabasesdata-sciencedata-structuresdata-vizdevopsdjangodockereditorsflaskfront-endgamedevguimachine-learningnumpyprojectspythontestingtoolsweb-devweb-scraping

Table of Contents

Recommended Video Course
Python Interfaces: Object-Oriented Design Principles

Implementing an Interface in Python

Implementing an Interface in Python

byWilliam Murphyadvancedpython

Table of Contents

Remove ads

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Python Interfaces: Object-Oriented Design Principles

Interfaces play an important role in software engineering. As an application grows, updates and changes to the code base become more difficult to manage. More often than not, you wind up having classes that look very similar but are unrelated, which can lead to some confusion. In this tutorial, you’ll see how you can use aPython interface to help determine what class you should use to tackle the current problem.

In this tutorial, you’ll be able to:

  • Understand how interfaces work and the caveats of Python interface creation
  • Comprehend how useful interfaces are in a dynamic language like Python
  • Implement an informal Python interface
  • Useabc.ABCMeta and@abc.abstractmethod to implement a formal Python interface

Interfaces in Python are handled differently than in most other languages, and they can vary in their design complexity. By the end of this tutorial, you’ll have a better understanding of some aspects of Python’s data model, as well as how interfaces in Python compare to those in languages like Java, C++, and Go.

Free Bonus:5 Thoughts On Python Mastery, a free course for Python developers that shows you the roadmap and the mindset you’ll need to take your Python skills to the next level.

Take the Quiz: Test your knowledge with our interactive “Implementing an Interface in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Implementing an Interface in Python

Interactive Quiz

Implementing an Interface in Python

In this quiz, you'll test your understanding of Python interfaces and their role in software engineering. You'll learn how interfaces can help manage complexity in a growing application and how to implement them in Python.

Python Interface Overview

At a high level, an interface acts as ablueprint for designing classes. Like classes, interfaces define methods. Unlike classes, these methods are abstract. Anabstract method is one that the interface simply defines. It doesn’t implement the methods. This is done by classes, which thenimplement the interface and give concrete meaning to the interface’s abstract methods.

Python’s approach to interface design is somewhat different when compared to languages likeJava, Go, andC++. These languages all have aninterface keyword, while Python does not. Python further deviates from other languages in one other aspect. It doesn’t require the class that’s implementing the interface to define all of the interface’s abstract methods.

Informal Interfaces

In certain circumstances, you may not need the strict rules of a formal Python interface. Python’s dynamic nature allows you to implement aninformal interface. An informal Python interface is a class that defines methods that can be overridden, but there’s no strict enforcement.

In the following example, you’ll take the perspective of adata engineer who needs to extract text from various different unstructured file types, like PDFs and emails. You’ll create an informal interface that defines the methods that will be in both thePdfParser andEmlParser concrete classes:

Python
classInformalParserInterface:defload_data_source(self,path:str,file_name:str)->str:"""Load in the file for extracting text."""passdefextract_text(self,full_file_name:str)->dict:"""Extract text from the currently loaded file."""pass

InformalParserInterface defines the two methods.load_data_source() and.extract_text(). These methods are defined but not implemented. The implementation will occur once you createconcrete classes that inherit fromInformalParserInterface.

As you can see,InformalParserInterface looks identical to a standardPython class. You rely onduck typing to inform users that this is an interface and should be used accordingly.

Note: Haven’t heard ofduck typing? This term says that if you have an object that looks like a duck, walks like a duck, and quacks like a duck, then it must be a duck! To learn more, check outDuck Typing.

With duck typing in mind, you define two classes that implement theInformalParserInterface. To use your interface, you must create a concrete class. Aconcrete class is a subclass of the interface that provides an implementation of the interface’s methods. You’ll create two concrete classes to implement your interface. The first isPdfParser, which you’ll use to parse the text fromPDF files:

Python
classPdfParser(InformalParserInterface):"""Extract text from a PDF"""defload_data_source(self,path:str,file_name:str)->str:"""Overrides InformalParserInterface.load_data_source()"""passdefextract_text(self,full_file_path:str)->dict:"""Overrides InformalParserInterface.extract_text()"""pass

The concrete implementation ofInformalParserInterface now allows you to extract text fromPDF files.

Note: The concrete implementation consists of apass statement, which does nothing. The implementation of the methods is not the point of this tutorial: Instead, you should focus on the structure of the classes.

The second concrete class isEmlParser, which you’ll use to parse the text from emails:

Python
classEmlParser(InformalParserInterface):"""Extract text from an email"""defload_data_source(self,path:str,file_name:str)->str:"""Overrides InformalParserInterface.load_data_source()"""passdefextract_text_from_email(self,full_file_path:str)->dict:"""A method defined only in EmlParser.        Does not override InformalParserInterface.extract_text()        """pass

The concrete implementation ofInformalParserInterface now allows you to extract text from email files.

So far, you’ve defined twoconcrete implementations of theInformalPythonInterface. However, note thatEmlParser fails to properly define.extract_text(), instead it defines a different.extract_text_from_email(). If you were to check whetherEmlParser implementsInformalParserInterface, then you’d get the following result:

Python
>>># Check if both PdfParser and EmlParser implement InformalParserInterface>>>issubclass(PdfParser,InformalParserInterface)True>>>issubclass(EmlParser,InformalParserInterface)True

This would returnTrue, which poses a bit of a problem since it violates the definition of an interface!

Now check themethod resolution order (MRO) ofPdfParser andEmlParser. This tells you the superclasses of the class in question, as well as the order in which they’re searched for executing a method. You can view a class’s MRO by using the dunder methodcls.__mro__:

Python
>>>PdfParser.__mro__(__main__.PdfParser, __main__.InformalParserInterface, object)>>>EmlParser.__mro__(__main__.EmlParser, __main__.InformalParserInterface, object)

Such informal interfaces are fine for small projects where only a few developers are working on the source code. However, as projects get larger and teams grow, this could lead to developers spending countless hours looking for hard-to-find logic errors in the codebase!

Using Metaclasses

Ideally, you would wantissubclass(EmlParser, InformalParserInterface) to returnFalse when the implementing class doesn’t define all of the interface’s abstract methods. To do this, you’ll create ametaclass calledParserMeta. You’ll be overriding twodunder methods:

  1. .__instancecheck__()
  2. .__subclasscheck__()

In the code block below, you create a class calledUpdatedInformalParserInterface that builds from theParserMeta metaclass:

Python
classParserMeta(type):"""A Parser metaclass that will be used for parser class creation.    """def__instancecheck__(cls,instance):returncls.__subclasscheck__(type(instance))def__subclasscheck__(cls,subclass):return(hasattr(subclass,'load_data_source')andcallable(subclass.load_data_source)andhasattr(subclass,'extract_text')andcallable(subclass.extract_text))classUpdatedInformalParserInterface(metaclass=ParserMeta):"""This interface is used for concrete classes to inherit from.    There is no need to define the ParserMeta methods as any class    as they are implicitly made available via .__subclasscheck__().    """pass

Now thatParserMeta andUpdatedInformalParserInterface have been created, you can create your concrete implementations.

First, create a new class for parsing PDFs calledPdfParserNew:

Python
classPdfParserNew:"""Extract text from a PDF."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides UpdatedInformalParserInterface.load_data_source()"""passdefextract_text(self,full_file_path:str)->dict:"""Overrides UpdatedInformalParserInterface.extract_text()"""pass

Here,PdfParserNew overrides.load_data_source() and.extract_text(), soissubclass(PdfParserNew, UpdatedInformalParserInterface) should returnTrue.

In this next code block, you have a new implementation of the email parser calledEmlParserNew:

Python
classEmlParserNew:"""Extract text from an email."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides UpdatedInformalParserInterface.load_data_source()"""passdefextract_text_from_email(self,full_file_path:str)->dict:"""A method defined only in EmlParser.        Does not override UpdatedInformalParserInterface.extract_text()        """pass

Here, you have a metaclass that’s used to createUpdatedInformalParserInterface. By using a metaclass, you don’t need to explicitly define the subclasses. Instead, the subclass mustdefine the required methods. If it doesn’t, thenissubclass(EmlParserNew, UpdatedInformalParserInterface) will returnFalse.

Runningissubclass() on your concrete classes will produce the following:

Python
>>>issubclass(PdfParserNew,UpdatedInformalParserInterface)True>>>issubclass(EmlParserNew,UpdatedInformalParserInterface)False

As expected,EmlParserNew is not a subclass ofUpdatedInformalParserInterface since.extract_text() wasn’t defined inEmlParserNew.

Now, let’s have a look at the MRO:

Python
>>>PdfParserNew.__mro__(<class '__main__.PdfParserNew'>, <class 'object'>)

As you can see,UpdatedInformalParserInterface is a superclass ofPdfParserNew, but it doesn’t appear in the MRO. This unusual behavior is caused by the fact thatUpdatedInformalParserInterface is avirtual base class ofPdfParserNew.

Using Virtual Base Classes

In the previous example,issubclass(PdfParserNew, UpdatedInformalParserInterface) returnedTrue, even thoughUpdatedInformalParserInterface did not appear in thePdfParserNew MRO. That’s becauseUpdatedInformalParserInterface is avirtual base class ofPdfParserNew.

The key difference between these and standard subclasses is that virtual base classes use the.__subclasscheck__() dunder method to implicitly check if a class is a virtual subclass of the superclass. Additionally, virtual base classes don’t appear in the subclass MRO.

Take a look at this code block:

Python
classPersonMeta(type):"""A person metaclass"""def__instancecheck__(cls,instance):returncls.__subclasscheck__(type(instance))def__subclasscheck__(cls,subclass):return(hasattr(subclass,'name')andcallable(subclass.name)andhasattr(subclass,'age')andcallable(subclass.age))classPersonSuper:"""A person superclass"""defname(self)->str:passdefage(self)->int:passclassPerson(metaclass=PersonMeta):"""Person interface built from PersonMeta metaclass."""pass

Here, you have the setup for creating your virtual base classes:

  1. The metaclassPersonMeta
  2. The base classPersonSuper
  3. The Python interfacePerson

Now that the setup for creatingvirtual base classes is done you’ll define two concrete classes,Employee andFriend. TheEmployee class inherits fromPersonSuper, whileFriend implicitly inherits fromPerson:

Python
# Inheriting subclassesclassEmployee(PersonSuper):"""Inherits from PersonSuper    PersonSuper will appear in Employee.__mro__    """passclassFriend:"""Built implicitly from Person    Friend is a virtual subclass of Person since    both required methods exist.    Person not in Friend.__mro__    """defname(self):passdefage(self):pass

AlthoughFriend does not explicitly inherit fromPerson, it implements.name() and.age(), soPerson becomes avirtual base class ofFriend. When you runissubclass(Friend, Person) it should returnTrue, meaning thatFriend is a subclass ofPerson.

The followingUML diagram shows what happens when you callissubclass() on theFriend class:

virtual base class

Taking a look atPersonMeta, you’ll notice that there’s another dunder method called.__instancecheck__(). This method is used to check if instances ofFriend are created from thePerson interface. Your code will call.__instancecheck__() when you useisinstance(Friend, Person).

Formal Interfaces

Informal interfaces can be useful for projects with a small code base and a limited number of programmers. However, informal interfaces would be the wrong approach for larger applications. In order to create aformal Python interface, you’ll need a few more tools from Python’sabc module.

Usingabc.ABCMeta

To enforce the subclass instantiation of abstract methods, you’ll utilize Python’s builtinABCMeta from theabc module. Going back to yourUpdatedInformalParserInterface interface, you created your own metaclass,ParserMeta, with the overridden dunder methods.__instancecheck__() and.__subclasscheck__().

Rather than create your own metaclass, you’ll useabc.ABCMeta as the metaclass. Then, you’ll overwrite.__subclasshook__() in place of.__instancecheck__() and.__subclasscheck__(), as it creates a more reliable implementation of these dunder methods.

Using.__subclasshook__()

Here’s the implementation ofFormalParserInterface usingabc.ABCMeta as your metaclass:

Python
importabcclassFormalParserInterface(metaclass=abc.ABCMeta):@classmethoddef__subclasshook__(cls,subclass):return(hasattr(subclass,'load_data_source')andcallable(subclass.load_data_source)andhasattr(subclass,'extract_text')andcallable(subclass.extract_text))classPdfParserNew:"""Extract text from a PDF."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides FormalParserInterface.load_data_source()"""passdefextract_text(self,full_file_path:str)->dict:"""Overrides FormalParserInterface.extract_text()"""passclassEmlParserNew:"""Extract text from an email."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides FormalParserInterface.load_data_source()"""passdefextract_text_from_email(self,full_file_path:str)->dict:"""A method defined only in EmlParser.        Does not override FormalParserInterface.extract_text()        """pass

If you runissubclass() onPdfParserNew andEmlParserNew, thenissubclass() will returnTrue andFalse, respectively.

Usingabc to Register a Virtual Subclass

Once you’ve imported theabc module, you can directlyregister a virtual subclass by using the.register() metamethod. In the next example, you register the interfaceDouble as a virtual base class of the built-in__float__ class:

Python
classDouble(metaclass=abc.ABCMeta):"""Double precision floating point number."""passDouble.register(float)

You can check out the effect of using.register():

Python
>>>issubclass(float,Double)True>>>isinstance(1.2345,Double)True

By using the.register() meta method, you’ve successfully registeredDouble as a virtual subclass offloat.

Once you’ve registeredDouble, you can use it as classdecorator to set the decorated class as a virtual subclass:

Python
@Double.registerclassDouble64:"""A 64-bit double-precision floating-point number."""passprint(issubclass(Double64,Double))# True

The decorator register method helps you to create a hierarchy of custom virtual class inheritance.

Using Subclass Detection With Registration

You must be careful when you’re combining.__subclasshook__() with.register(), as.__subclasshook__() takes precedence over virtual subclass registration. To ensure that the registered virtual subclasses are taken into consideration, you must addNotImplemented to the.__subclasshook__() dunder method. TheFormalParserInterface would be updated to the following:

Python
classFormalParserInterface(metaclass=abc.ABCMeta):@classmethoddef__subclasshook__(cls,subclass):return(hasattr(subclass,'load_data_source')andcallable(subclass.load_data_source)andhasattr(subclass,'extract_text')andcallable(subclass.extract_text)orNotImplemented)classPdfParserNew:"""Extract text from a PDF."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides FormalParserInterface.load_data_source()"""passdefextract_text(self,full_file_path:str)->dict:"""Overrides FormalParserInterface.extract_text()"""pass@FormalParserInterface.registerclassEmlParserNew:"""Extract text from an email."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides FormalParserInterface.load_data_source()"""passdefextract_text_from_email(self,full_file_path:str)->dict:"""A method defined only in EmlParser.        Does not override FormalParserInterface.extract_text()        """passprint(issubclass(PdfParserNew,FormalParserInterface))# Trueprint(issubclass(EmlParserNew,FormalParserInterface))# True

Since you’ve used registration, you can see thatEmlParserNew is considered a virtual subclass of yourFormalParserInterface interface. This is not what you wanted sinceEmlParserNew doesn’t override.extract_text().Please use caution with virtual subclass registration!

Using Abstract Method Declaration

Anabstract method is a method that’s declared by the Python interface, but it may not have a useful implementation. The abstract method must be overridden by the concrete class that implements the interface in question.

To create abstract methods in Python, you add the@abc.abstractmethod decorator to the interface’s methods. In the next example, you update theFormalParserInterface to include the abstract methods.load_data_source() and.extract_text():

Python
classFormalParserInterface(metaclass=abc.ABCMeta):@classmethoddef__subclasshook__(cls,subclass):return(hasattr(subclass,'load_data_source')andcallable(subclass.load_data_source)andhasattr(subclass,'extract_text')andcallable(subclass.extract_text)orNotImplemented)@abc.abstractmethoddefload_data_source(self,path:str,file_name:str):"""Load in the data set"""raiseNotImplementedError@abc.abstractmethoddefextract_text(self,full_file_path:str):"""Extract text from the data set"""raiseNotImplementedErrorclassPdfParserNew(FormalParserInterface):"""Extract text from a PDF."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides FormalParserInterface.load_data_source()"""passdefextract_text(self,full_file_path:str)->dict:"""Overrides FormalParserInterface.extract_text()"""passclassEmlParserNew(FormalParserInterface):"""Extract text from an email."""defload_data_source(self,path:str,file_name:str)->str:"""Overrides FormalParserInterface.load_data_source()"""passdefextract_text_from_email(self,full_file_path:str)->dict:"""A method defined only in EmlParser.        Does not override FormalParserInterface.extract_text()        """pass

In the above example, you’ve finally created a formal interface that will raise errors when the abstract methods aren’t overridden. ThePdfParserNew instance,pdf_parser, won’t raise any errors, asPdfParserNew is correctly overriding theFormalParserInterface abstract methods. However,EmlParserNew will raise an error:

Python
>>>pdf_parser=PdfParserNew()>>>eml_parser=EmlParserNew()Traceback (most recent call last):...TypeError:Can't instantiate abstract class EmlParserNew with abstract methods extract_text

As you can see, thetraceback message tells you that you haven’t overridden all the abstract methods. This is the behavior you expect when building a formal Python interface.

Interfaces in Other Languages

Interfaces appear in many programming languages, and their implementation varies greatly from language to language. In the next few sections, you’ll compare interfaces in Python to Java, C++, and Go.

Java

Unlike Python,Java contains aninterface keyword. Keeping with the file parser example, you declare an interface in Java like so:

Java
publicinterfaceFileParserInterface{// Static fields, and abstract methods go here ...publicvoidloadDataSource();publicvoidextractText();}

Now you’ll create two concrete classes,PdfParser andEmlParser, to implement theFileParserInterface. To do so, you must use theimplements keyword in the class definition, like so:

Java
publicclassEmlParserimplementsFileParserInterface{publicvoidloadDataSource(){// Code to load the data set}publicvoidextractText(){// Code to extract the text}}

Continuing with your file parsing example, a fully-functional Java interface would look something like this:

Java
importjava.util.*;importjava.io.*;publicclassFileParser{publicstaticvoidmain(String[]args)throwsIOException{// The main entry point}publicinterfaceFileParserInterface{HashMap<String,ArrayList<String>>file_contents=null;publicvoidloadDataSource();publicvoidextractText();}publicclassPdfParserimplementsFileParserInterface{publicvoidloadDataSource(){// Code to load the data set}publicvoidextractText(){// Code to extract the text}}publicclassEmlParserimplementsFileParserInterface{publicvoidloadDataSource(){// Code to load the data set}publicvoidextractText(){// Code to extract the text}}}

As you can see, a Python interface gives you much more flexibility during creation than a Java interface does.

C++

Like Python, C++ uses abstract base classes to create interfaces. When defining an interface in C++, you use the keywordvirtual to describe a method that should be overwritten in the concrete class:

C++
classFileParserInterface{public:virtualvoidloadDataSource(std::stringpath,std::stringfile_name);virtualvoidextractText(std::stringfull_file_name);};

When you want to implement the interface, you’ll give the concrete class name, followed by a colon (:), and then the name of the interface. The following example demonstrates C++ interface implementation:

C++
classPdfParser:FileParserInterface{public:voidloadDataSource(std::stringpath,std::stringfile_name);voidextractText(std::stringfull_file_name);};classEmlParser:FileParserInterface{public:voidloadDataSource(std::stringpath,std::stringfile_name);voidextractText(std::stringfull_file_name);};

A Python interface and a C++ interface have some similarities in that they both make use of abstract base classes to simulate interfaces.

Go

Although Go’s syntax is reminiscent of Python, the Go programming language contains aninterface keyword, like Java. Let’s create thefileParserInterface in Go:

Go
typefileParserInterfaceinterface{loadDataSet(pathstring,filenamestring)extractText(full_file_pathstring)}

A big difference between Python and Go is that Go doesn’t have classes. Rather, Go is similar toC in that it uses thestruct keyword to create structures. Astructure is similar to a class in that a structure contains data and methods. However, unlike a class, all of the data and methods are publicly accessed. The concrete structs in Go will be used to implement thefileParserInterface.

Here’s an example of how Go uses interfaces:

Go
packagemaintypefileParserInterfaceinterface{loadDataSet(pathstring,filenamestring)extractText(full_file_pathstring)}typepdfParserstruct{// Data goes here ...}typeemlParserstruct{// Data goes here ...}func(ppdfParser)loadDataSet(){// Method definition ...}func(ppdfParser)extractText(){// Method definition ...}func(eemlParser)loadDataSet(){// Method definition ...}func(eemlParser)extractText(){// Method definition ...}funcmain(){// Main entrypoint}

Unlike a Python interface, a Go interface is created using structs and the explicit keywordinterface.

Conclusion

Python offers great flexibility when you’re creating interfaces. An informal Python interface is useful for small projects where you’re less likely to get confused as to what the return types of the methods are. As a project grows, the need for aformal Python interface becomes more important as it becomes more difficult to infer return types. This ensures that the concrete class, which implements the interface, overwrites the abstract methods.

Now you can:

  • Understandhow interfaces work and the caveats of creating a Python interface
  • Understand theusefulness of interfaces in a dynamic language like Python
  • Implementformal and informal interfaces in Python
  • Compare Python interfaces to those in languages like Java, C++, and Go

Now that you’ve become familiar with how to create a Python interface, add a Python interface to your next project to see its usefulness in action!

Take the Quiz: Test your knowledge with our interactive “Implementing an Interface in Python” quiz. You’ll receive a score upon completion to help you track your learning progress:


Implementing an Interface in Python

Interactive Quiz

Implementing an Interface in Python

In this quiz, you'll test your understanding of Python interfaces and their role in software engineering. You'll learn how interfaces can help manage complexity in a growing application and how to implement them in Python.

Watch Now This tutorial has a related video course created by the Real Python team. Watch it together with the written tutorial to deepen your understanding:Python Interfaces: Object-Oriented Design Principles

🐍 Python Tricks 💌

Get a short & sweetPython Trick delivered to your inbox every couple of days. No spam ever. Unsubscribe any time. Curated by the Real Python team.

Python Tricks Dictionary Merge

AboutWilliam Murphy

William has been working with Python for over 6 years, working in roles such as data scientist, machine learning engineer, data engineer, and dev ops engineer. He is currently a senior software engineering consultant at ModernDay Productions.

» More about William

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

MasterReal-World Python Skills With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

MasterReal-World Python Skills
With Unlimited Access to Real Python

Locked learning resources

Join us and get access to thousands of tutorials, hands-on video courses, and a community of expert Pythonistas:

Level Up Your Python Skills »

What Do You Think?

Rate this article:

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students.Get tips for asking good questions andget answers to common questions in our support portal.


Looking for a real-time conversation? Visit theReal Python Community Chat or join the next“Office Hours” Live Q&A Session. Happy Pythoning!

Keep Learning

Related Topics:advancedpython

Recommended Video Course:Python Interfaces: Object-Oriented Design Principles

Related Tutorials:

Keep reading Real Python by creating a free account or signing in:

Already have an account?Sign-In

Almost there! Complete this form and click the button below to gain instant access:

Python Logo

5 Thoughts On Python Mastery

🔒 No spam. We take your privacy seriously.


[8]ページ先頭

©2009-2025 Movatter.jp