python factory mode

Posted by Randwulf on Thu, 27 Jan 2022 08:29:54 +0100

background

I've been looking at the source code of the transformers package recently. How can I not look at this package if I do nlp. There are many interesting problems in the process. I'll share them with you here.
Problem encountered: when I look at a code block that automatically loads the model in the transformers package and use pycharm to view the source code, I will locate the source code to position 1, but when I use the debugger, the source code is defined to position 2. Then I wonder how this is realized.

Then I put the problem into the group. A group friend said it might be the factory mode of python. I don't know if I can solve this problem, but I find that this model is something I haven't encountered before. Therefore, let me share the introduction of this model here.

article

  1. Source: https://segmentfault.com/a/1190000013053013
  2. Author: Xia Qiu
  3. Note: in case of infringement, contact me to delete.

Implementing design pattern with Python -- factory pattern

preface

Factory mode, as its name implies, is that we can obtain the required "products" through a specified "factory". In design mode, it is mainly used for the creation process of abstract objects, so that users can specify the objects they want without caring about the instantiation process of objects. The advantage of this is that users only need to obtain an object instance through a fixed interface rather than directly calling the instantiation method of the class, which hides the complexity of the instance creation process, decouples the code of production instance and use instance, and reduces the complexity of maintenance.
This article will use Python to implement simple examples of three factory modes, all of which are hosted on Github.

Simple factory

First, let's look at a simple factory example:

#coding=utf-8
class Mercedes(object):
    """mercedes
    """
    def __repr__(self):
        return "Mercedes-Benz"

class BMW(object):
    """bmw
    """
    def __repr__(self):
        return "BMW"

Suppose we have two "products", Mercedes and BMW cars. If there is no "factory" to produce them, we need to instantiate them in the code, such as:

mercedes = Mercedes()
bmw = BMW()

But in reality, you may face many automotive products, and the construction parameters of each product are different, so you will encounter trouble when creating instances. At this time, a "simple factory" can be constructed to encapsulate the instantiation process of all cars.

class SimpleCarFactory(object):
    """Simple factory
    """
    @staticmethod
    def product_car(name):
        if name == 'mb':
            return Mercedes()
        elif name == 'bmw':
            return BMW()

With the SimpleCarFactory class, you can obtain the desired object instance by passing parameters to the fixed interface, as follows:

c1 = SimpleCarFactory.product_car('mb')
c2 = SimpleCarFactory.product_car('bmw')

Factory method

Although there is a simple factory, we will find new problems in the actual use of the factory: if we want to add a "product", such as Audi's car, we need to modify the product in SimpleCarFactory in addition to adding an Audi class_ Car method. This violates the opening and closing principle in software design [1], that is, try not to modify the original code when extending new classes. Therefore, we abstract SimpleCarFactory into different factories on the basis of simple factories, and each factory generates its own products accordingly. This is the factory method.

#coding=utf-8
import abc

class AbstractFactory(object):
    """Abstract factory
    """
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def product_car(self):
        pass

class MercedesFactory(AbstractFactory):
    """Mercedes factory
    """
    def product_car(self):
        return Mercedes()

class BMWFactory(AbstractFactory):
    """BMW factory
    """
    def product_car(self):
        return BMW()

We abstract the factory and implement an abstract base class AbstractFactory with abc module [2], so that we can obtain specific product instances through specific factories:

c1 = MercedesFactory().product_car()
c2 = BMWFactory().product_car()

Each factory is responsible for producing its own products, which also avoids the need to modify the factory code when we add new products, but only add the corresponding factory. If you add an Audi product, you only need to add an Audi class and an Audi factory class.

Abstract factory

Although the factory method solves the problem of "modifying code", if we want to produce many products, we will find that we also need to write many corresponding factory classes. For example, if Mercedes factory and BMWFactory produce not only cars, but also SUVs, we need to construct two more factory classes for SUVs using the factory method. Therefore, in order to solve this problem, we need to further abstract the factory class, so that a factory can produce multiple products of the same class, which is the abstract factory. The specific implementation is as follows:

#coding=utf-8
import abc

# Two kinds of cars
class Mercedes_C63(object):
    """mercedes C63
    """
    def __repr__(self):
        return "Mercedes-Benz: C63"

class BMW_M3(object):
    """bmw M3
    """
    def __repr__(self):
        return "BMW: M3"

#Two SUV s
class Mercedes_G63(object):
    """mercedes G63
    """
    def __repr__(self):
        return "Mercedes-Benz: G63"

class BMW_X5(object):
    """bmw X5
    """
    def __repr__(self):
        return "BMW: X5"

class AbstractFactory(object):
    """Abstract factory
    It can produce cars as well as cars SUV
    """
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def product_car(self):
        pass

    @abc.abstractmethod
    def product_suv(self):
        pass

class MercedesFactory(AbstractFactory):
    """Mercedes factory
    """
    def product_car(self):
        return Mercedes_C63()

    def product_suv(self):
        return Mercedes_G63()

class BMWFactory(AbstractFactory):
    """BMW factory
    """
    def product_car(self):
        return BMW_M3()

    def product_suv(self):
        return BMW_X5()

We let the base class AbstractFactory produce cars and SUVs at the same time, and then let Mercedes factory and BMWFactory inherit AbstractFactory and rewrite product_car and product_suv method is enough.

c1 = MercedesFactory().product_car()
s1 = MercedesFactory().product_suv()
print(c1, s1)
s2 = BMWFactory().product_suv()
c2 = BMWFactory().product_car()
print(c2, s2)

The biggest difference between the abstract factory pattern and the factory method pattern is that a factory object in the abstract factory can be responsible for the creation of multiple different product objects, which is simpler and more efficient than the factory method pattern.

conclusion

When learning the design mode, you will be confused about the practical application of the three factory modes. In fact, the three modes have their own advantages and disadvantages, and the application scenarios are different:

The simple factory mode is applicable to the case where there are few objects to be created and the business logic in the factory method will not be too complex. Moreover, users only care about the type of instance to be created and do not care about its initialization process, such as the instance of multiple databases (MySQL/MongoDB), the parser of multiple format files (XML/JSON), etc.
The factory method pattern inherits the advantages of the simple factory pattern and has been improved. It is no longer responsible for the creation of all products through a factory class, but hand over the specific creation work to the corresponding subclasses, which makes the factory method pattern allow the system to expand more efficiently. In practical application, it can be used to realize the system log system, such as specific program operation log, network log, database log, etc. can be created with specific factory classes.
Based on the factory method, the abstract factory mode extends the factory's support for the creation of multiple products, and is more suitable for some large-scale systems. For example, there are more than one product family in the system, and the products of these product families need to implement the same interface, such as different buttons, text boxes, fonts and so on under different themes in many software system interfaces.

reference material

  1. https://zh.wikipedia.org/wiki/%E5%BC%80%E9%97%AD%E5%8E%9F%E5%88%99
  2. https://docs.python.org/2/library/abc.html

Topics: Python Back-end