python通过覆盖__new__()方法来控制对象的创建。
if not hasattr(cls, ‘instance’):方法hasattr用于查看对象cls是否具有属性instance, 该属性的作用是检查该类是否已经生成了一个对象。
class singleton(object):
def __new__(cls):
if not hasattr(cls, 'instance'):
cls.instance = super(singleton, cls).__new__(cls)
return cls.instance
s = Singleton()
print("Object created", s)
s1 = Singleton()
print("Object created", s1)
运行结果:
Object created <main.Singleton object at 0x7eff5af722e8>
Object created <main.Singleton object at 0x7eff5af722e8>
懒汉式实例化能够确保在实际需要时才创建对象,有效节约资源。
class Singleton:
__instance = None
def __init__(self):
if not Singleton.__instance:
print("__init__ method called..")
else:
print("Instance already created:", \
self.getInstance())
@classmethod
def getInstance(cls):
if not cls.__instance:
cls.__instance = Singleton()
return cls.__instance
s1 = Singleton()
print("Object created,", Singleton.getInstance())
s2 = Singleton()
print("Object created,", Singleton.getInstance())
运行结果:
init method called…
init method called…
Object created, <main.Singleton object at 0x7eff5a74c978>
Instance already created: <main.Singleton object at 0x7eff5a74c978>
Object created, <main.Singleton object at 0x7eff5a74c978>
通常程序员需要的是让实例共享相同的状态
开发人员应该关注状态和行为, 而不是同一性
由于Monostate单例模式是基于所有对象共享相同状态,因此也被称为单态模式
与单例模式不同的地方: 创建两个实例b, b1, 会生成两个不同的对象,而单例模式只能生成一个对象
然而对象的状态,即b.__dict__和b1.__dict__却是相同的
class Borg:
__shared_state = {"1": "2"}
def __init__(self):
self.x = 1
self.__dict__ = self.__shared_state
pass
b = Borg()
b1 = Borg()
b.x = 4
print("Borg Object 'b' : ", b)
print("Borg Object 'b1': ", b1)
print("Borg state 'b' : ", b.__dict__)
print("Borg state 'b1': ", b1.__dict__)
运行结果:
Borg Object ‘b’ : <main.Borg object at 0x7eff5a74c908>
Borg Object ‘b1’: <main.Borg object at 0x7eff5a74c400>
Borg state ‘b’ : {‘1’: ‘2’, ‘x’: 4}
Borg state ‘b1’: {‘1’: ‘2’, ‘x’: 4}
除此之外, 我们还可以通过修改__new__方法本身来实现 Borg 模式
class Borg(object):
_shared_state = {"a":"as"}
def __new__(cls, *args, **kwargs):
obj = super(Borg, cls).__new__(cls, *args, **kwargs)
obj.__dict__ = cls._shared_state()
return obj
类的定义由它的元类决定,所以当我们用类 A 创建一个类时,Python 通过A=Type(name, bases, dict)创建它
# 假设一个类int 有预定义的元类MyInt
class MyInt(type):
def __call__(cls, *args, **kwds):
print("****** Here's My int ******", args)
print("Now do whatever you want with these objects...")
return type.__call__(cls, *args, **kwds)
class int(metaclass=MyInt):
def __init__(self, x, y):
self.x = x
self.y = y
i = int(4, 5)
运行结果:
****** Here’s My int ****** (4, 5)
Now do whatever you want with these objects…
对于已经存在的类来说,当需要创建对象时,将调用Python的__call__()方法
在这段代码中, 当我们使用int(4, 5)实例化类int时,元类MyInt的__call__()方法
将被调用,这意味着元类控制着对象是的实例化
由于元类对类的创建和对象实例化有更多的控制权,所以它可以用于创建单例
注意:为了控制类的创建和初始化,元类将覆盖__new__和__init__方法
以下示例代码是基于元类的单例实现
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(MetaSingleton, \
cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Logger(metaclass=MetaSingleton):
pass
logger1 = Logger()
logger2 = Logger()
print(logger1)
print(logger2)
运行结果:
<main.Logger object at 0x7eff5af9ec18>
<main.Logger object at 0x7eff5af9ec18>
以需要对数据库进行多种读取和写入操作的云服务为例
完整的云服务被分解为多个服务,每个服务执行不同的数据库操作
针对UI(Web应用程序)上的操作将导致调用API, 最终产生相应的DB操作
很明显,跨不同服务的共享资源是数据库本身,因次,设计云服务需要注意:
import sqlite3
class MetaSingleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(MetaSingleton, \
cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=MetaSingleton):
connection = None
def connect(self):
if self.connection is None:
self.connection = sqlite3.connect("db.sqlite3")
self.cursorobj = self.connection.cursor()
return self.cursorobj
db1 = Database().connect()
db2 = Database().connect()
print("Database Objects DB1", db1)
print("Database Objects DB2", db2)
运行结果:
Database Objects DB1
Database Objects DB2
我们发现:
但是,加入我们要开发的不是单个Web应用程序,而是集群化,即多个Web应用共享一个数据库
这样单例在这种情况下就不好使了,因为每新增一个Web应用程序,就要新建一个单例,添加一个新的对象来查询数据库,这导致数据库无法同步,并且要消耗大量的资源
这种情况下,数据库连接池比实现单例要好得多
为基础设施提供运行状况监控服务(就像Nagios一样)
我们创建HealthCheck类,它作为单例实现。我们还要维护一个被监控的服务器列表。
当一个服务器从这个列表中删除时,监控软件应该察觉到这一情况,并从被监控的服务器列表中将其删除
class HealthCheck:
_instance = None
def __new__(cls, *args, **kwargs):
if not HealthCheck._instance:
HealthCheck._instance = super(HealthCheck, \
cls).__new__(cls, *args, **kwargs)
return HealthCheck._instance
def __init__(self):
self._servers = []
def addServer(self):
self._servers.append("Server 1")
self._servers.append("Server 2")
self._servers.append("Server 3")
self._servers.append("Server 4")
def changeServer(self):
self._servers.pop()
self._servers.append("Server 5")
hc1 = HealthCheck()
hc2 = HealthCheck()
hc1.addServer()
print("Schedules health check for servers (1)..")
for i in range(4):
print("Checking ", hc1._servers[i])
hc2.changeServer()
print("Schedules health check for servers (2)..")
for i in range(4):
print("Checking ", hc2._servers[i])
运行结果:
Schedules health check for servers (1)…
Checking Server 1
Checking Server 2
Checking Server 3
Checking Server 4
Schedules health check for servers (2)…
Checking Server 1
Checking Server 2
Checking Server 3
Checking Server 5
changeServer()方法会删除最后一个服务器,并想计划进行运行状况检查的基础设施中添加一个新的服务器Server 5。因此,当运行状况检查进行第二次迭代时,它会使用修改后的服务器列表。
由于单例具有全局访问权限,因此可能会出现以下问题:
a. 在许多应用程序中,我们只需创建一个对象,如线程池、缓存、对话框、注册表设置等。如果为每个应用程序创建多个实例,则会导致资源过度使用。单例模式在这种情况下工作得很好。
b. 单例是一种经过时间考验的成熟方法,能够在不带来太多缺陷的情况下提供全局访问点。
c. 经典的单例模式:允许进行多次实例化,但最终只返回同一个对象
d. Borg或Monostate模式:这是单例模式的变体。Borg允许创建相同状态的多个对象,这与GoF描述的单例模式有所不同
e. Web应用:单例模式可以用于在多个服务间实现一致的数据操作
f. 最后,我们研究了单例可能出现的错误,以及开发人员需要避免的情况