如何以 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 中轻松实现单例类设计模式的三个重要关键字:
- 私有对象实例
- 私有构造器
- 制作一个静态公共函数,以便其他外部模块/java文件可以访问该方法来获取SingletonClass的对象/实例来执行操作或获取一些数据值。
但是,在 Python 中,我们没有 private 关键字。那么我们如何在 Python 中实现单例类设计模式呢?
用 Python 实现的单例类设计模式
有多种实现方法,但在本文中,我将讨论两种主要方法,它们通常用于在 Python 中实现单例类设计模式。
让我们开始!
第一种方法非常简单,我们将检查该类的对象/实例是否已经创建。如果是,那么我们将不允许创建该类的另一个实例。让我们看看这是一个动作:
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__ 方法将不会被调用。