Python设计模式之单例模式实例


Posted in Python onApril 26, 2014

注:使用的是Python 2.7。

一个简单实现

class Foo(object):
    __instance = None
    def __init__(self):
        pass
    @classmethod
    def getinstance(cls):
        if(cls.__instance == None):
            cls.__instance = Foo()
        return cls.__instance
if __name__ == '__main__':
    foo1 = Foo.getinstance()
    foo2 = Foo.getinstance()
    print id(foo1)
    print id(foo2)
    print id(Foo())

输出的前两个结果是相同的(id(foo1)与id(foo2)的值相同),第三个结果和前两个不同。这里类方法getinstance()用于获取单例,但是类本身也可以实例化,这样的方式其实并不符合单例模式的要求。但是这样做也有好处,代码简单,大家约定好这样子调用就行了。但是最好在类的命名上也体现了出来这是一个单例类,例如Foo_singleton。

换一个思路

先说一下init和new的区别:

class Foo(object):
    __instance = None
    def __init__(self):
        print 'init'
if __name__ == '__main__':
    foo = Foo()

运行结果是:
init

而下面的示例:
class Foo(object):
    __instance = None
    def __init__(self):
        print 'init'
    def __new__(cls, *args, **kwargs):
        print 'new'
if __name__ == '__main__':
    foo = Foo()

运行结果是:
new

new是一个类方法,会创建对象时调用。而init方法是在创建完对象后调用,对当前对象的实例做一些一些初始化,无返回值。如果重写了new而在new里面没有调用init或者没有返回实例,那么init将不起作用。以下内容引用自http://docs.python.org/2/reference/datamodel.html#object.new

If __new__() returns an instance of cls, then the new instance's __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__().
If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked.

这样做:
class Foo(object):
    __instance = None
    def __init__(self):
        print 'init'
    def __new__(cls, *args, **kwargs):
        print 'new'
        if cls.__instance == None:
            cls.__instance = cls.__new__(cls, *args, **kwargs)
        return cls.__instance
if __name__ == '__main__':
    foo = Foo()

    错误如下:

RuntimeError: maximum recursion depth exceeded in cmp

而这样也有一样的错误:

class Foo(object):
    __instance = None
    def __init__(self):
        if self.__class__.__instance == None:
            self.__class__.__instance = Foo()
        print 'init'
if __name__ == '__main__':
    foo = Foo()

该怎么做呢?

下面参考了http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python/31887#31887:

class Foo(object):
    __instance = None
    def __new__(cls, *args, **kwargs):
        print 'hhhhhhhhh'
        if not cls.__instance:
            cls.__instance = super(Foo, cls).__new__(cls, *args, **kwargs)
        return cls.__instance
    def hi(self):
        print 'hi, world'
        print 'hi, letian'
if __name__ == '__main__':
    foo1 = Foo()
    foo2 = Foo()
    print id(foo1)
    print id(foo2)
    print isinstance(foo1, object)
    print isinstance(foo1, Foo)
    foo1.hi()

运行结果:
hhhhhhhhh
hhhhhhhhh
39578896
39578896
True
True
hi, world
hi, letian

那么,到底发生了什么,我们先回顾一下super:
>>> print super.__doc__
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)

可以肯定上面的单例模式代码中的这一行代码:
cls.__instance = super(Foo, cls).__new__(cls, *args, **kwargs)

super(Foo, cls)是object,super(Foo, cls).new方法使用的是object的new方法。我们看一下object.new方法的作用:
>>> print object.__new__.__doc__
T.__new__(S, ...) -> a new object with type S, a subtype of T

如果是一个继承链

class Fo(object):
    def __new__(cls, *args, **kwargs):
        print 'hi, i am Fo'
        return  super(Fo, cls).__new__(cls, *args, **kwargs)
class Foo(Fo):
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            print Foo is cls
            print issubclass(cls, Fo)
            print issubclass(cls, object)
            cls.__instance = super(Foo, cls).__new__(cls, *args, **kwargs)
        return cls.__instance
    def hi(self):
        print 'hi, world'
if __name__ == '__main__':
    foo1 = Foo()
    foo1.hi()
    print isinstance(foo1, Foo)
    print isinstance(foo1, Fo)
    print isinstance(foo1, object)

运行结果如下:
True
True
True
hi, i am Fo
hi, world
True
True
True

如果如下定义Fo,也正常运行:
class Fo(object):
    pass

但是,若这样定义:
class Fo(object):
    def __new__(cls, *args, **kwargs):
        print 'hi, i am Fo'

运行时报错如下:
AttributeError: 'NoneType' object has no attribute 'hi'
Python 相关文章推荐
从零学Python之入门(二)基本数据类型
May 25 Python
Python对两个有序列表进行合并和排序的例子
Jun 13 Python
使用python加密自己的密码
Aug 04 Python
简单解决Python文件中文编码问题
Nov 22 Python
快速了解Python中的装饰器
Jan 11 Python
基于python requests库中的代理实例讲解
May 07 Python
python下载库的步骤方法
Oct 12 Python
python3的UnicodeDecodeError解决方法
Dec 20 Python
快速查找Python安装路径方法
Feb 06 Python
python实现图片转换成素描和漫画格式
Aug 19 Python
解决PyCharm IDE环境下,执行unittest不生成测试报告的问题
Sep 03 Python
PyTorch 如何检查模型梯度是否可导
Jun 05 Python
Python设计模式之观察者模式实例
Apr 26 #Python
Python设计模式之代理模式实例
Apr 26 #Python
python中的列表推导浅析
Apr 26 #Python
Python中的Numpy入门教程
Apr 26 #Python
Python中的map、reduce和filter浅析
Apr 26 #Python
Python实现的Kmeans++算法实例
Apr 26 #Python
爬山算法简介和Python实现实例
Apr 26 #Python
You might like
个人站长制做网页常用的php代码
2007/03/03 PHP
yii2中dropDownList实现二级和三级联动写法
2017/04/26 PHP
laravel框架数据库操作、查询构建器、Eloquent ORM操作实例分析
2019/12/20 PHP
IE/FireFox具备兼容性的拖动代码
2007/08/13 Javascript
js 多浏览器分别判断代码
2010/04/01 Javascript
原生js操作checkbox用document.getElementById实现
2013/10/12 Javascript
js 剪切板应用clipboardData详细解析
2013/12/17 Javascript
JS实现图片产生波纹一样flash效果的方法
2015/02/27 Javascript
JQuery跳出each循环的方法
2015/04/16 Javascript
JS+CSS实现的简单折叠展开多级菜单效果
2015/09/12 Javascript
创建自己的jquery表格插件
2015/11/25 Javascript
jQuery增加与删除table列的方法
2016/03/01 Javascript
window.onload绑定多个事件的两种解决方案
2016/05/15 Javascript
javascript弹出窗口中增加确定取消按钮
2016/06/24 Javascript
JS实现数字格式千分位相互转换方法
2016/08/01 Javascript
Angularjs 双向绑定时字符串的转换成数字类型的问题
2017/06/12 Javascript
详解如何构建Promise队列实现异步函数顺序执行
2018/10/23 Javascript
Vuex的API文档说明详解
2020/02/05 Javascript
vue项目中企业微信使用js-sdk时config和agentConfig配置方式详解
2020/12/15 Vue.js
[56:21]LGD vs IG 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
Python学习小技巧之列表项的拼接
2017/05/20 Python
Python+opencv 实现图片文字的分割的方法示例
2019/07/04 Python
python list多级排序知识点总结
2019/10/23 Python
基于h5py的使用及数据封装代码
2019/12/26 Python
pytorch中的transforms模块实例详解
2019/12/31 Python
cookies应对python反爬虫知识点详解
2020/11/25 Python
利用Python批量识别电子账单数据的方法
2021/02/08 Python
自荐书模板
2013/12/19 职场文书
父亲追悼会答谢词
2014/01/17 职场文书
公司保密承诺书
2014/03/27 职场文书
小学社团活动总结
2014/06/27 职场文书
在校大学生自我评价范文
2014/09/12 职场文书
家长高考寄语
2015/02/27 职场文书
2015年小班保育员工作总结
2015/05/27 职场文书
实习证明模板
2015/06/16 职场文书
win10系统xps文件怎么打开?win10打开xps文件的两种操作方法
2022/07/23 数码科技