设计模式中所说的单例模式,是指确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这样的类称为单例类,单例模式也是一种对象创建型模式。

单例模式也是面试中涉及设计模式环节最常问到的一个模式,因为其简单理解,也经常作为一个设计模式教学视频的最开始切入设计模式的例子。

比如数据库连接池,网站的计数器。这些都可以采用单例模式来实现。

首先来看一下Python中是如何创建对象的。

class User(object):

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '''{"name": "%s"}''' % self.name


user1 = User("Alice")
user2 = User("Bob")
print("user1 address: {%s} str: %s" % (id(user1), str(user1)))
print("user2 address: {%s} str: %s" % (id(user2), str(user2)))
user1 address: {2481456} str: {"name": "Alice"}
user2 address: {2481552} str: {"name": "Bob"}

很明显能看出两个对象的内存地址不同,内容也是不同的。说明我们创建了两个实例。那么我们可以通过下面的代码去构建单例模式。

class User(object):
    __instance = None

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '''{"name": "%s"}''' % self.name

    @classmethod
    def get_instance(cls, name):
        if cls.__instance is None:
            cls.__instance = User(name)
        cls.__instance.name = name
        return cls.__instance


user1 = User.get_instance("Alice")
print("user1 address: {%s} str: %s" % (id(user1), str(user1)))
user2 = User.get_instance("Bob")
print("user1 address: {%s} str: %s" % (id(user1), str(user1)))
print("user2 address: {%s} str: %s" % (id(user2), str(user2)))
user1 address: {4316560} str: {"name": "Alice"}
user1 address: {4316560} str: {"name": "Bob"}
user2 address: {4316560} str: {"name": "Bob"}

通过这里的输出,我们发现 user1 和 user2 的内存地址是一样的,也就是表明了这里只创建了一个实例。然而这个地方还是有一个问题,如果有好事之徒执行 user3 = User("Del"),那这里将创建一个新的实例。也就是并没有真正的单例模式,有人称之为伪单例模式。

class User(object):
    __instance = None

    def __init__(self, name):
        self.name = name

    def __str__(self):
        return '''{"name": "%s"}''' % self.name

    def __new__(cls, name):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls)
        cls.__instance.name = name
        return cls.__instance


user1 = User("Alice")
print("user1 address: {%s} str: %s" % (id(user1), str(user1)))
user2 = User("Bob")
print("user1 address: {%s} str: %s" % (id(user1), str(user1)))
print("user2 address: {%s} str: %s" % (id(user2), str(user2)))

而可以通过 __new__ 方法来处理这个问题。

而这里有一个地方需要注意,程序在执行 user1 = User("Alice") 时会先调用 __new__ 方法,然后继续执行 __init__ 方法,最后通过将对象返回给 user1,这里是 __new__ 方法的机制。