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 相关文章推荐
使用C语言来扩展Python程序和Zope服务器的教程
Apr 14 Python
Python while、for、生成器、列表推导等语句的执行效率测试
Jun 03 Python
利用Python学习RabbitMQ消息队列
Nov 30 Python
python实现发送和获取手机短信验证码
Jan 15 Python
python基础教程之匿名函数lambda
Jan 17 Python
Python 实现随机数详解及实例代码
Apr 15 Python
python try except 捕获所有异常的实例
Oct 18 Python
在python中使用with打开多个文件的方法
Jan 07 Python
django中间键重定向实例方法
Nov 10 Python
Python操作Sonqube API获取检测结果并打印过程解析
Nov 27 Python
pycharm开发一个简单界面和通用mvc模板(操作方法图解)
May 27 Python
Python实现一个简单的递归下降分析器
Aug 01 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
全国FM电台频率大全 - 20 广西省
2020/03/11 无线电
2020显卡排行榜天梯图 显卡天梯图2020年3月最新版
2020/04/02 数码科技
个人写的PHP验证码生成类分享
2014/08/21 PHP
使用PHP实现阻止用户上传成人照片或者裸照
2014/12/25 PHP
php简单获取目录列表的方法
2015/03/24 PHP
利用PHP判断是否是连乘数字串的方法示例
2017/07/03 PHP
浅谈laravel5.5 belongsToMany自身的正确用法
2019/10/17 PHP
基于JQuery的密码强度验证代码
2010/03/01 Javascript
基于jquery实现后台左侧菜单点击上下滑动显示
2013/04/11 Javascript
jQuery数据缓存功能的实现思路及简单模拟
2013/05/27 Javascript
jquery toolbar与网页浮动工具条具体实现代码
2014/01/12 Javascript
Jquery实现自定义tooltip示例代码
2014/02/12 Javascript
浅谈JavaScript中定义变量时有无var声明的区别
2014/08/18 Javascript
JQuery中clone方法复制节点
2015/05/18 Javascript
json+jQuery实现的无限级树形菜单效果代码
2015/08/27 Javascript
AngularJS 最常用的八种功能(基础知识)
2017/06/26 Javascript
Vue.js实现按钮的动态绑定效果及实现代码
2017/08/21 Javascript
详解Vue组件实现tips的总结
2017/11/01 Javascript
vue使用axios实现文件上传进度的实时更新详解
2017/12/20 Javascript
JavaScript之解构赋值的理解
2019/01/30 Javascript
JavaScript常用内置对象用法分析
2019/07/09 Javascript
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
2019/10/23 Javascript
Vue简单封装axios之解决post请求后端接收不到参数问题
2020/02/16 Javascript
Python输出由1,2,3,4组成的互不相同且无重复的三位数
2018/02/01 Python
python使用mysql的两种使用方式
2018/03/07 Python
python中的文件打开与关闭操作命令介绍
2018/04/26 Python
详解Appium+Python之生成html测试报告
2019/01/04 Python
python实现UDP协议下的文件传输
2020/03/20 Python
JackJones官方旗舰店:杰克琼斯男装
2018/03/27 全球购物
what is the difference between ext2 and ext3
2013/11/03 面试题
物流专业专科生职业生涯规划书
2014/09/14 职场文书
副总经理岗位职责范本
2014/09/30 职场文书
政审证明范文
2015/06/19 职场文书
导游词之镇江西津古渡
2019/11/06 职场文书
vue实现滑动解锁功能
2022/03/03 Vue.js
Java 使用类型为Object的变量指向任意类型的对象
2022/04/13 Java/Android