如何在Python中实现Java那样的单例? - Vidip

21-07-21 banq

如何以 Pythonic 的方式实现单例设计模式
单例是日常编程的重要设计模式,经常用于不同的项目中。
尽管单例模式在 Java 中是相当流行的模式,但是在 Python 中,它并不是那么简单,单例类可以通过不同的方式实现。我们将探索在 Python 中实现 Singleton 的不同方式。对于那些对 Singleton Class 概念不太满意的人,我已经尝试在下面对其进行简要概述。
单例类是指只有一个对象实例的类。简单来说,只能初始化一个对象实例或该类的一个对象。它隐藏了类的构造函数,因此只能创建该类的一个实例。
它提供对其实例的全局访问,并且可以控制 Class 实例对象的使用。单例设计模式有助于多个现实世界的用例。
单例类是如何在 Java 中实现的?

public class SingletonClass
{
    private static SingletonClass SingleInstanceObject = null;
    private SingletonClass(){};
    public static SingletonClass getSingleInstance() {
        if (SingleInstanceObject != null) {
            return SingleInstanceObject;
        }
        else {
            SingleInstanceObject =  new SingletonClass();
            return SingleInstanceObject;
        }
    }
    protected void testFunction() {
        System.out.println("Accessing method through single object instance");
    }
}

有助于在 Java 中轻松实现单例类设计模式的三个重要关键字:
  1. 私有对象实例
  2. 私有构造器
  3. 制作一个静态公共函数,以便其他外部模块/java文件可以访问该方法来获取SingletonClass的对象/实例来执行操作或获取一些数据值。

但是,在 Python 中,我们没有 private 关键字。那么我们如何在 Python 中实现单例类设计模式呢?
 

用 Python 实现的单例类设计模式
有多种实现方法,但在本文中,我将讨论两种主要方法,它们通常用于在 Python 中实现单例类设计模式。
让我们开始!

  • 1.第一种方法:单例类的简单实现

第一种方法非常简单,我们将检查该类的对象/实例是否已经创建。如果是,那么我们将不允许创建该类的另一个实例。让我们看看这是一个动作:

class Singleton:
    __private_instance = None
    def __init__(self):
        if Singleton.__private_instance is None: //4
            Singleton.__private_instance = self
        else:
            print("Can not create instance of this class")
            
    @staticmethod
    def get_instance():
        if Singleton.__private_instance is None:
            Singleton()
        else:
            return Singleton.__private_instance
            
    def do_something(self):
        print("accessing instance method")
        
first_object = Singleton.get_instance()
first_object.do_something() //20
a = Singleton()


上面代码会运行异常:
accessing instance methodException will be raised

正如您在上面的代码中看到的,在第 (20) 行,它尝试创建 Singleton 类的新实例,但是,在构造函数(第 4 行)中,它检查是否已经存在实例(如果存在)那么它会引发一个异常。
在python中双下划线(__some_variable)变量作为私有变量之前,这个不能在类外访问。因此没有其他模块/类可以直接访问或修改它。我们在第 (3) 行的构造函数也充当虚拟私有构造函数。
这意味着虽然没有像我们在 Java 中那样的 private 关键字,但是通过条件逻辑我们可以检查实例是否已经创建,因此它充当私有构造函数。因此,上面的代码是 Singleton 类的简单实现,但不是最好的。
  • 2.另一种实现Singleton Class的方法

借助__new__ dunder 方法,我们可以实现 Singleton 类。我已经解释了 __new__ dunder 方法的概念,对于那些不熟悉它的人,vola!好吧,我已经在下面介绍了:
问:Python 中的 __new__ Dunder 方法是什么?
答。__new__ dunder 方法是在 __init__ 方法(​​构造函数)之前调用的类方法。
1# __new__ 方法被调用来创建对象。此方法始终在 __init__ 方法之前调用。它将类作为参数,因为它必须创建该类的实例。默认情况下,它是一个静态方法。无需为其添加静态方法装饰器。
2#__init__ 方法被调用来初始化对象
现在,我们将使用 __new__ Dunder 方法的实用程序来实现单例设计模式。
让我们看看 __new__ 方法的实际作用以及它如何帮助我们创建一个单例类:

class Singleton(object):
    __private_instance = None

    def __new__(cls, *args, **kwargs):
        print("new method called")
        if not cls.__private_instance:
            cls.__private_instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
            return cls.__private_instance
        else:
            return None

    def __init__(self):
        print("init method is called")
            
    @staticmethod
    def get_instance():
        print("get instance method called")
        if Singleton.__private_instance is None:
            Singleton() //19
        return Singleton.__private_instance
            
    def do_something(self):
        print("accessing instance method")
        
ins = Singleton.get_instance() //25
ins.do_something()


你能猜出上面代码的打印语句的输出:

get instance method called 
new method called
init method is called
accessing instance method

在第 (25) 行,调用一个静态方法来获取类的单个实例,然后如果__private_instance包含实例值,它将不会创建新实例,否则它将通过调用 Singleton 类来创建新实例(在第 (19) 行) )。因此,它首先转到__new__方法并在第 (7) 行创建一个新实例,然后转到__init__方法以初始化实例对象的任何值。然后,在第 (26) 行,一个对象调用实例方法并打印“访问实例方法”。
现在,让我们尝试在上面的示例中创建另一个对象实例,会发生什么?

class Singleton(object):
    __private_instance = None

    def __new__(cls, *args, **kwargs):
        print("new method called")
        if not cls.__private_instance:
            cls.__private_instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
            return cls.__private_instance
        else:
            return None

    def __init__(self):
        print("init method is called")
            
    @staticmethod
    def get_instance():
        print("get instance method called")
        if Singleton.__private_instance is None:
            Singleton()
        return Singleton.__private_instance
            
    def do_something(self):
        print("accessing instance method")
        
ins = Singleton.get_instance()
ins.do_something()
Singleton() //27


如您所见,现在我添加了Singleton()(在第 (27) 行)来创建 Singleton 类的对象。现在输出的变化应该是什么?
我会说!稍等,不要向下滚动,首先尝试猜测输出。
上面代码示例的输出是:

get instance method called
new method called
init method is called
accessing instance method
new method called


正如你所看到的,由于第 (27) 行的 Singleton() 调用,它首先转到 __new__ 方法,但是因为已经为 Singleton 类创建了实例,因此它没有创建另一个实例,并返回 None,每当 __new__ 方法返回 -> None 时, __init__ 方法将不会被调用。

猜你喜欢