博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python中的单例模式实现
阅读量:5064 次
发布时间:2019-06-12

本文共 3367 字,大约阅读时间需要 11 分钟。

原文地址:

使用Python来实现单例模式是一件很有意思的事情,在这个过程中我们会重新审视Python在创建类(Build Class)和构建实例时都调用了那些magic method

同时我也想说的是Python的单例模块实现多种多样,但有些实现是存在问题的,在某些情况下是不可用或者影响系统的某些功能的。

1.利用Python的metaclass实现单例

先说我认为实现起来最地道的Python Singleton的实现方法。metaclass在Python中是一个很奇特的东西。Python在类的构建过程中会寻找metaclass,默认是type,如果我们在类中定义的metaclass,Python在类的构建中就会使用这个metaclass。

上面的描述主要体现在下面的代码逻辑中。

# ceval.cstatic PyObject *  build_class(PyObject *methods, PyObject *bases, PyObject *name) {    PyObject *metaclass = NULL, *result, *base;    // 0.先尝试从用户自定义的类中查询__metaclass__    if (PyDict_Check(methods))          metaclass = PyDict_GetItemString(methods, "__metaclass__");    if (metaclass != NULL)          Py_INCREF(metaclass);    else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {          // 1.从基类获取__class__作为metaclass          base = PyTuple_GET_ITEM(bases, 0);          metaclass = PyObject_GetAttrString(base, "__class__");          if (metaclass == NULL) {              PyErr_Clear();              // 2.使用ob_type              metaclass = (PyObject *)base->ob_type;              Py_INCREF(metaclass);          }     }     else {          // 3.如果这个类连基类都没有,使用全局global的__metaclass__          PyObject *g = PyEval_GetGlobals();          if (g != NULL && PyDict_Check(g))              metaclass = PyDict_GetItemString(g, "__metaclass__");          if (metaclass == NULL)              // 4.使用PyClass_Type              metaclass = (PyObject *) &PyClass_Type;          Py_INCREF(metaclass);      }  ......

metaclass创建好之后,我们要使用metaclass创建对应的类,并初始化创建处理的类。

类的创建主要在type_call是实现,我们在大体提到了type_call里面的部分逻辑,主要涉及tp_newtp_init。之后,在初始化类中会调用PyObject_Call方法。PyObject_Call方法会调用metaclass的tp_call方法,并且返回此类对应的实例,因此为了实现单例模式,我们可以在tp_call这个方法上做手脚。

class Singleton(type):        _instance = {}        def __call__(cls, *args, **kwargs):              if cls not in Singleton._instance:                  Singleton._instance[cls] = type.__call__(cls, *args, **kwargs)              return Singleton._instance[cls]   class A(object):         __metaclass__ = Singleton    a1 = A()    a2 = A()    print id(a1) == id(a2)

上面实例a1和实例a2的id是相同。例外__call__即使上面所只得tp_call。

2.利用__new__实现单例模式

另一种方法是我们在tp_new的时候去检查此类有没有被创建,创建了就直接返回对应的实例。

class Singleton(object):     _instance = None     def __new__(cls, *args, **kwargs):           if Singleton._instance is None:               Singleton._instance = object.__new__(cls, *args, **kwargs)           return Singleton._instance class A(Singleton):     def __init__(self):           print "init A"  a1 = A()  a2 = A()  print id(a1) == id(a2)

上面这种方法也可以实现单例,同时也存在着风险。

  1. 如果子类重载了__new__方法,那么父类的__new__方法就不起作用了
  2. 如果运行上面的代码会发现打印出两次"init A", 因为在这种情况下tp_new会被调用两次,第二次由于_instance不等于None,直接返回。相同tp_init也会被调用两次,每次都会调用A的__init__方法。如果不小心在__init__中做了一些初始化的逻辑,那么每次构建一个实例都要进行初始化了(是不是感觉和单例模式的初衷相违背了呢)。

3.利用修饰器实现单例模式

_instance = {} def Singleton(cls):      def wrapper(*args, **kwargs):           if cls not in _instance:               _instance[cls] = cls(*args, **kwargs)           return _instance[cls]      return wrapper  @Singleton  class A(object):        def __init__(self):             print "init"   a = A()    b = A()    print id(a)   print id(b)   print type(A)

运行上面的代码,一切看起来都合情合理,"init"也只打印了一次。但看最后一行的输出,A的类型为function。好的,显然没有错,我们用修饰器修饰了。但A的类型变化了会产生什么负面作用是很难把握的。随着工程的扩大,A是什么类型开发者可能已经不了解了,因此也会导致很多问题出来,并且很难去定位问题。

4.小结

上面三种方法其实都可以实现单例模式,就个人经验和喜好来说,第一种最好。

转载于:https://www.cnblogs.com/whosemario/p/5841901.html

你可能感兴趣的文章
Python 3.X 练习集100题 05
查看>>
设计器 和后台代码的转换 快捷键
查看>>
Monkey测试结果分析
查看>>
STL——配接器、常用算法使用
查看>>
STL容器之vector
查看>>
无法向会话状态服务器发出会话状态请求
查看>>
数据中心虚拟化技术
查看>>
01入门
查看>>
复习文件操作
查看>>
SQL Server 使用作业设置定时任务之一(转载)
查看>>
第二阶段冲刺-01
查看>>
BZOJ1045 HAOI2008 糖果传递
查看>>
发送请求时params和data的区别
查看>>
JavaScript 克隆数组
查看>>
eggs
查看>>
一步步学习微软InfoPath2010和SP2010--第七章节--从SP列表和业务数据连接接收数据(4)--外部项目选取器和业务数据连接...
查看>>
如何增强你的SharePoint 团队网站首页
查看>>
FZU 1914 Funny Positive Sequence(线性算法)
查看>>
oracle 报错ORA-12514: TNS:listener does not currently know of service requested in connec
查看>>
基于grunt构建的前端集成开发环境
查看>>