How to use Python interface based programming method to realize it

  • 2021-12-13 08:27:37
  • OfStack

Directory first through an instance to understand the interface to solve what problems. Define 1 interface Introduction to the Python Abstract Base Class for Defining Classes and Inheriting Interfaces (PEP3119)

In the software industry, the only constant is change. The product manager will change, the product requirements will change, and the code will also change.

Different code designs have different workload caused by changes. Some require almost one refactoring every time they change, while others only need to modify one configuration file or add one line of code to the class. Of course, better code design, because of its good scalability, high cohesion and low coupling, is easy to maintain and change with less variation. If we can have good code design, we need to learn design patterns. What I want to share with you today is how to program based on interfaces in Python.

In 1994, GoF's "Design Pattern" 1 book has an important principle: programming based on interface rather than implementation. The English source text is "Program to an interface, not an implementaion". interface mentioned here is not an interface in a specific programming language, it is language-independent, and refers to a function list provided by developers to users. Interface is implemented by keyword interface in java language. java does not support multiple inheritance of classes, but supports multiple inheritance of interfaces. Developers of java are very familiar with interfaces. Python does not need the design of Java at all, but can learn from the advantages of interfaces.

First, through an instance to understand what problems the interface solves.

For example, you are implementing a picture upload function, which is currently stored in 7 Niu Cloud. Your class may be like this.


class QnyImageStore(object):

    def getToken():
        pass

    def upload_qny(self,image):
        print("Qny upload.")
        #do something
    
    def download_qny(self,image):
        print("Qny download.")
        #do something

In actual development, there are many lines of code and more than three functions, which are called in hundreds of places and scattered in hundreds of files. After a period of time, the company built its own private cloud, requiring that it can no longer use 7 Niu Cloud and change it to its own cloud storage, so you have to rewrite a class:


class OwnImageStore(object):

    def upload_own(self,image):
        print("Qny upload.")
        #do something
    
    def download_own(self,image):
        print("Qny download.")
        #do something

Then you use 7 cows to go to the place, all replace, but also replace the name of the function, and finally test many times, which one place in life is not considered, and the operation reports errors. I finally changed it. Suddenly, one day, the demand changed again. Because my cloud service often went wrong, I had to change to Alibaba Cloud. After the pain of the last turn, I saw that this time I had to change it again and vomited blood directly.

In fact, the problem is that the code you write is not general enough and the naming is not abstract enough. If you use upload and download for the function name of your class, then the amount of code you modify may be reduced to 1.5, just replace the class name under 1. In fact, we can use interfaces to reduce the amount of code change: encapsulate unstable implementations and expose stable interfaces through patterns where interfaces and implementations are separated. When the upstream and downstream systems use the functions developed by us, they only need to use the function list declared in the interface, so that when the implementation changes, the code of the upstream system basically does not need to be changed, so as to reduce the coupling and improve the scalability. The following provides an interface-based code implementation for this problem.

Define 1 interface


from abc import ABCMeta, abstractmethod

class ImageStore(metaclass = ABCMeta):
    
    @abstractmethod
    def upload(self,image): 
        pass
        #raise NotImplementedError

    @abstractmethod
    def download(self,image): 
        pass
        #raise NotImplementedError

After the interface is defined, any class that inherits the interface must override the upload and download methods in order to use it correctly, otherwise an exception will be thrown. Here we don't need to throw an exception in the interface ourselves, and the standard library abc has done this for us.

Defining classes, inheriting interfaces

The purpose is actually to enforce constraints, which means that upload and download methods must be implemented and checked at compile time to ensure the robustness of the program.


class OwnImageStore2(ImageStore):

    def upload(self,image):
        print("Own upload.")
        #do something
    
    def download(self,image):
        print("Own download.")
        #do something

class QnyImageStore2(ImageStore):

    def getToken():
        pass

    def upload(self,image):
        print("Qny upload.")

    def download(self,image):
        print("Qny download.")
        #do something

Next, we define an interface that automatically selects the method that invokes the corresponding object based on the type.


class UsedStore(object):

    def __init__(self, store):
        if not isinstance(store, ImageStore): raise Exception('Bad interface')
        self._store = store

    def upload(self):
        self._store.upload('image')

    def download(self):
        self._store.download('image')

Finally, we can indicate in the configuration file which specific interface we are using:


# In other files, you should call 
img = QnyImageStore2()
# img = OwnImageStore2()  Put these in the configuration file, and you only need to update the configuration file to replace them  
store = UsedStore(img)
store.upload()
store.download()

In this way, after adding new picture storage, we only need to add corresponding classes, inherit interfaces, and modify the configuration file, which can reduce a lot of code modification work.

Introduction to the Python Abstract Base Class (PEP3119)

The python standard library abc, whose full name is Abstract Base Classes, provides the following functions:

A method of overloading isinstance () and issubclass () 1 new module abc as "Abstract Base Classes Support Framework". It defines a metaclass for abc and a decorator that can be used to define abstract methods Specific abstract base classes for containers and iterators will be added to the collections module

Fundamentals:

In the field of object-oriented programming, design patterns that interact with objects can be divided into two basic categories, namely, "call" and "check".

Calling refers to interacting with an object by calling its methods. Typically, this is used in conjunction with polymorphism, so calling a given method might run different code depending on the type of object.

Checking is the ability of external code (outside of an object's methods) to check the type or properties of the object and decide what to do with the object based on that information.

Both usage patterns serve the same common purpose, that is, they can support processing a variety of and possibly novel objects in a unified way, but at the same time allow customization of processing decisions for each different type of object.

In the classic OOP theory, invocation is the preferred design pattern, and inspection is discouraged because inspection is considered a product of an earlier procedural programming style. In practice, however, this view is too dogmatic and rigid, leading to a design rigidity that is quite different from the dynamic nature of languages such as Python.

In particular, it is usually necessary to deal with objects in a way that the creator of the object class cannot expect. Built into every object method that meets the needs of every possible user of the object is not always the best solution. Moreover, there are many powerful scheduling philosophies that stand in sharp contrast to the classical OOP behavioral requirements that are strictly encapsulated in objects, such as rule-or pattern-matching-driven logic

On the other hand, one of the criticisms of censorship by classical OOP theorists is the lack of formalism and the special nature of the inspected content. In a language such as Python, almost any aspect of an object can be reflected and directly accessed through external code, and there are many different ways to test whether an object conforms to a specific protocol. For example, if you ask, "Is this object a mutable sequence container?" You can look for the base class for the list, or you can look for a method named "getitem". But please note that although these tests may seem obvious, they are not correct because one of them will produce a false negative and the other will produce a false positive.

The generally agreed remedy is to standardize the tests and group them into formal forms. This is easiest to do through an inheritance mechanism or some other way, by associating a standard set of testable properties with each class. Each test comes with a set of commitments: it contains commitments about the class's general behavior, as well as commitments about other available class methods.

PEP proposes a special strategy for organizing these tests called Abstract Base Classes (ABC). ABC is simply an Python class added to the object's inheritance tree to send some of the object's functionality to an external inspector. The test is done using isinstance (), and the presence of a specific ABC means that the test has passed.

In addition, ABC defines the minimum set of methods for establishing the behavior of this type of feature. Code that distinguishes objects by their ABC type can be trusted that these methods will always exist. Each of these methods comes with the generalized abstract semantic definitions described in the ABC documentation. The semantic definitions of these standards are not mandatory, but they are strongly recommended.

Like everything else in Python, these commitments are of a gentleman's nature, which in this case means that although the language does implement some of the commitments made in ABC, the implementer of the concrete class must ensure that the rest remains.

After reading the above description, you can simply understand that ABC is a base class. Inheriting it, you can write an interface similar to java, and the methods in the interface will always exist, so you can use it with confidence without probing.

PEP3119 also gives you sample code to understand:


from abc import ABCMeta, abstractmethod

class A(metaclass=ABCMeta):
    @abstractmethod
    def foo(self): pass

A()  # raises TypeError

class B(A):
    pass

B()  # raises TypeError

class C(A):
    def foo(self): print(42)

C()  # works

Don't say much, I hope you can use ABC correctly. At the same time, it is highly recommended to learn Python, just look at the official documents of Python and the proposal of PEP. Here are the most authoritative explanations.

In addition, setting mode is also a very important programming skill and way. It is a basic skill. If the basic skill is not enough, you don't know how to appreciate and taste a fighter in front of you.

If you master the design pattern and look at other people's codes, you will have a critical eye, which are fighters and tractors, which is also very helpful for your own learning and improvement, and the written codes will be more maintainable, readable, extensible and flexible.


Related articles: