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命令行参数解析模块getopt使用实例
Apr 13 Python
Python网络编程中urllib2模块的用法总结
Jul 12 Python
Python实现并行抓取整站40万条房价数据(可更换抓取城市)
Dec 14 Python
一步步教你用Python实现2048小游戏
Jan 19 Python
Python 一键制作微信好友图片墙的方法
May 16 Python
python django model联合主键的例子
Aug 06 Python
python 通过文件夹导入包的操作
Jun 01 Python
matplotlib 生成的图像中无法显示中文字符的解决方法
Jun 10 Python
python中setuptools的作用是什么
Jun 19 Python
Python 读写 Matlab Mat 格式数据的操作
May 19 Python
关于pytest结合csv模块实现csv格式的数据驱动问题
May 30 Python
使用pd.merge表连接出现多余行的问题解决
Jun 16 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
服务器web工具 php环境下
2010/12/29 PHP
codeigniter使用技巧批量插入数据实例方法分享
2013/12/31 PHP
使用php转义输出HTML到JavaScript
2015/03/27 PHP
PHP获取文件扩展名的常用方法小结【五种方式】
2018/04/27 PHP
屏蔽F1~F12的快捷键的js函数
2010/05/06 Javascript
基于JQuery的访问WebService的代码(可访问Java[Xfire])
2010/11/19 Javascript
jquery随意添加移除html的实现代码
2011/06/21 Javascript
JavaScript打字小游戏代码
2011/12/26 Javascript
javascript中负数算术右移、逻辑右移的奥秘探索
2013/10/17 Javascript
理解Angular数据双向绑定
2016/01/10 Javascript
jQuery插件扩展extend的简单实现原理
2016/06/24 Javascript
springMVC结合AjaxForm上传文件
2016/07/12 Javascript
微信小程序 Audio API详解及实例代码
2016/09/30 Javascript
JS实现AES加密并与PHP互通的方法分析
2017/04/19 Javascript
以BootStrap Tab为例写一个前端组件
2017/07/25 Javascript
利用JQUERY实现多个AJAX请求等待的实例
2017/12/14 jQuery
jQuery EasyUI 选项卡面板tabs的使用实例讲解
2017/12/25 jQuery
vue-router判断页面未登录自动跳转到登录页的方法示例
2018/11/04 Javascript
详解react阻止无效重渲染的多种方式
2018/12/11 Javascript
[44:21]Ti4 循环赛第四日 附加赛NEWBEE vs LGD
2014/07/13 DOTA
Python采用socket模拟TCP通讯的实现方法
2014/11/19 Python
深入解析Python编程中super关键字的用法
2016/06/24 Python
python获取本机所有IP地址的方法
2018/12/26 Python
在Python 中实现图片加框和加字的方法
2019/01/26 Python
基于OpenCV python3实现证件照换背景的方法
2019/03/22 Python
Python学习笔记之抓取某只基金历史净值数据实战案例
2019/06/03 Python
GUESS德国官网:美国牛仔服装品牌
2017/02/14 全球购物
Otticanet澳大利亚:最顶尖的世界名牌眼镜, 能得到打折季的价格
2018/08/23 全球购物
什么是事务?事务有哪些性质?
2012/03/11 面试题
便利店投资创业计划书
2014/02/08 职场文书
《桥》教学反思
2014/04/09 职场文书
2014办公室副主任四风对照检查材料思想汇报
2014/09/20 职场文书
学生抄作业检讨书(2篇)
2014/10/17 职场文书
狼牙山五壮士观后感
2015/06/09 职场文书
2019银行竞聘书
2019/06/21 职场文书
Nginx中break与last的区别详析
2021/03/31 Servers