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 相关文章推荐
Django中更新多个对象数据与删除对象的方法
Jul 17 Python
解决nohup执行python程序log文件写入不及时的问题
Jan 14 Python
Django实现文件上传下载
Oct 06 Python
tensorflow如何继续训练之前保存的模型实例
Jan 21 Python
From CSV to SQLite3 by python 导入csv到sqlite实例
Feb 14 Python
Django 自定义404 500等错误页面的实现
Mar 08 Python
python 操作mysql数据中fetchone()和fetchall()方式
May 15 Python
基于PyTorch的permute和reshape/view的区别介绍
Jun 18 Python
python海龟绘图之画国旗实例代码
Nov 11 Python
python 利用 PIL 将数组值转成图片的实现
Apr 12 Python
Python提取PDF指定内容并生成新文件
Jun 09 Python
Python实现视频中添加音频工具详解
Dec 06 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
使用迭代器 遍历文件信息的详解
2013/06/08 PHP
解决laravel5中auth用户登录其他页面获取不到登录信息的问题
2019/10/08 PHP
php的RSA加密解密算法原理与用法分析
2020/01/23 PHP
背景音乐每次刷新都可以自动更换
2007/02/01 Javascript
javascript 时间比较实现代码
2009/10/28 Javascript
基于jquery的仿百度搜索框效果代码
2011/04/11 Javascript
jquery文本框中的事件应用以输入邮箱为例
2014/05/06 Javascript
Javascript核心读书有感之语句
2015/02/11 Javascript
通过正则表达式获取url中参数的简单实现
2016/06/07 Javascript
微信小程序开发之Tabbar实例详解
2017/01/09 Javascript
完美解决input[type=number]无法显示非数字字符的问题
2017/02/28 Javascript
JavaScript 巧学巧用
2017/05/23 Javascript
详解Vue2.x-directive的学习笔记
2017/07/17 Javascript
vue鼠标悬停事件实例详解
2019/04/01 Javascript
JS this关键字在ajax中使用出现问题解决方案
2020/07/17 Javascript
[03:01]DOTA2英雄基础教程 露娜
2014/01/07 DOTA
[01:13:01]2018DOTA2亚洲邀请赛 4.4 淘汰赛 TNC vs VG 第三场
2018/04/05 DOTA
pandas 取出表中一列数据所有的值并转换为array类型的方法
2018/04/11 Python
Python使用Selenium爬取淘宝异步加载的数据方法
2018/12/17 Python
Python 处理图片像素点的实例
2019/01/08 Python
Python基础学习之基本数据结构详解【数字、字符串、列表、元组、集合、字典】
2019/06/18 Python
python实现抠图给证件照换背景源码
2019/08/20 Python
基于Python获取docx/doc文件内容代码解析
2020/02/17 Python
python七种方法判断字符串是否包含子串
2020/08/18 Python
详解Anaconda安装tensorflow报错问题解决方法
2020/11/01 Python
EJB面试题
2015/07/28 面试题
应聘护士自荐信
2013/10/21 职场文书
师范大学音乐表演专业求职信
2013/10/23 职场文书
外贸学院会计专业应届生求职信
2013/11/14 职场文书
简历中求职的个人自我评价
2013/12/03 职场文书
违反课堂纪律检讨书
2014/01/19 职场文书
大班下学期幼儿评语
2014/12/30 职场文书
HTML+CSS制作心跳特效的实现
2021/05/26 HTML / CSS
Python MNIST手写体识别详解与试练
2021/11/07 Python
Spring Boot 使用 Spring-Retry 进行重试框架
2022/04/24 Java/Android
python神经网络 使用Keras构建RNN训练
2022/05/04 Python