设计模式中,最简单的一个就是 “单例模式”, 那么首先,就实现一下单例模式。

那么根据个人的理解,很快就写出第一版。

# -*- coding: utf-8 -*-

class Singleton(object):

    # 定义静态变量实例
__singleton = None def __init__(self):
pass @staticmethod
def get_instance():
if Singleton.__singleton is None:
Singleton.__singleton = Singleton()
return Singleton.__singleton

测试一下:

if __name__ == "__main__":
instance1 = Singleton.get_instance()
instance2 = Singleton.get_instance() print id(instance1)
print id(instance2)

liutrumpdeMacBook-Air:singleton trump$ python Singleton
4419778640
4419778640

看起来运行良好。但是其实,这里面有2个问题.

1. 这里类方法getinstance()用于获取单例,但是类本身也可以实例化,这样的方式其实并不符合单例模式的要求。

if __name__ == "__main__":
instance1 = Singleton.get_instance()
instance2 = Singleton.get_instance() instance3 = Singleton() print id(instance1)
print id(instance2)
print id(instance3) 执行结果:
liutrumpdeMacBook-Air:singleton trump$ python Singleton
4461824080
4461824080
4461824144

在c#或java的设计模式中,我们通常是通过私有化类的构造函数来杀死类本身的繁殖能力

然而python并没有访问限定强制约束, 那么怎么办呢?

这个后续再说.

但是这样做也有好处,代码简单,大家约定好这样子调用就行了。

但是最好在类的命名上也体现了出来这是一个单例类.

2. 这个单例类并不是线程安全的.

比如我写了如下的测试代码来测试它的线程安全性.

def test_singleton_in_thread():
print id(Singleton.get_instance()) if __name__ == "__main__":
idx = 0
while 1:
MyThread(test_singleton_in_thread, []).start()
idx += 1
if idx > 0X100:
break

很快,就发现这确实不是线程安全的....

关于问题1. 我们换个思路, 来谈一谈python里面的构造函数.(其实python里面并没有构造函数个概念,⊙﹏⊙, 叫习惯了而已)

python 里的__init__(self) 函数,之前一直被我认为是python类的构造函数 __del__(self), 一直被我认为是类的析构函数...

其实,这是不对滴.

这时候__new__(self)就要登场了.

我们看一下官方的介绍.

http://docs.python.org/2/reference/datamodel.html#object.new

这么鬼长,其实告诉我们了一个道理.

new是一个类方法,会创建对象时调用。而init方法是在创建完对象后调用,对当前对象的实例做一些一些初始化,无返回值。如果重写了new而在new里面没有调用init或者没有返回实例,那么init将不起作用。

我擦,类方法,又是什么鬼....

好吧,继续查下资料.

http://www.cnblogs.com/2gua/archive/2012/09/03/2668125.html

静态方法:无法访问类属性、实例属性,相当于一个相对独立的方法,跟类其实没什么关系,换个角度来讲,其实就是放在一个类的作用域里的函数而已。

类成员方法:可以访问类属性,无法访问实例属性。
类方法有类变量cls传入,从而可以用cls做一些相关的处理。并且有子类继承时,调用该类方法时,传入的类变量cls是子类,而非父类。 
 
了解了这些姿势以后,我们可以尝试通过改造类的__new__方法来给类进行计划生育了.
 

尝试了写了一下.

然而报一个递归溢出......

查了一下. 借鉴了一下

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

# -*- coding: utf-8 -*-

class Singleton(object):

    # 定义静态变量实例
__instance = None def __init__(self):
pass def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls.__instance if __name__ == "__main__":
instance1 = Singleton()
instance2 = Singleton() print id(instance1)
print id(instance2)

liutrumpdeMacBook-Air:singleton trump$ python Singleton3.py
4544985488
4544985488

这里不是很懂super的用法. 查了一下文档.

看起来是调用了object类的 __new__方法来构造出了我们需要的类.(类似于c#里的反射???,不知道python的解释器是如何实现的)

总之看起来是靠谱的。耶耶耶。

同样的, 这种写法依然不是线程安全的.

关于问题2.

为了保证在多线程下线程安全性。

我们在写单例模式时候, 通常使用双重检查锁定来检测实例是否存在。

为什么用double check, 请自行思考...

实现:

# -*- coding: utf-8 -*-
from MyThread import *
import threading Lock = threading.Lock() class Singleton(object): # 定义静态变量实例
__instance = None def __init__(self):
pass def __new__(cls, *args, **kwargs):
if not cls.__instance:
try:
Lock.acquire()
# double check
if not cls.__instance:
cls.__instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
finally:
Lock.release()
return cls.__instance def test_singleton_in_thread():
print id(Singleton()) if __name__ == "__main__":
idx = 0
while 1:
MyThread(test_singleton_in_thread, []).start()
idx += 1
if idx > 0X100:
break

运行结果:

证明是线程安全的. 欧耶.

上面的代码在单例模式中被称作,懒汉式单例.

还有一种称之为饿汉式单例.

遗憾的是,python下是没有办法实现的。

饿汉,太饿了, 一开始就把实例构造出来...是不是很形象.

贴个c#的代码,供参考.

解释: 声明一个私有static 成员实例并直接调用类默认静态构造函数实例化.

然后安插共有静态方法返回该实例.

使用CLR的静态成员只能在静态构造函数中构造并且只能构造一次的特性来实现了单例.

特别的直观和美观, 也保证了线程安全.

使用起来很方便.

但也有个弊端就是需要在类加载的时候就把实例给初始化出来.

当这个实例非常大或者构造很耗时的话此时的性能就会有影响.

通常情况,这种写法是使用最多的写法(反正我写的话肯定不想使用double check...)

class Singleton
{
private static Singleton instance = new EagerSingleton(); private Singleton() { } public static Singleton GetInstance()
{
return instance;
}
}

总结:

本文介绍了使用python来实现不能约束构造实例只能通过规约指定方法来实现的单例模式,并由此引申控制类的__new__函数来

约束类构造的实例。

本文并且讨论了多线程下的double check 的单例模式的实现。

本文并且讨论了单例模式的懒汉式实现以及饿汉式实现.

好,第一个最简单的设计模式的python实现就到这里。

to be continued.

[python实现设计模式]-1. 单例模式的更多相关文章

  1. Python:设计模式介绍--单例模式

    单例模式 1.单例是只有一个实例2.通过静态字段+静态字段伪造出一个单例效果3.什么时候用:当所有实例中封装的数据相同时,创建单例模式(eg:连接池) 用单例模式创建连接池: class CP: __ ...

  2. Python与设计模式之单例模式

    一.什么是单例 即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了 # setti ...

  3. python设计模式之单例模式(一)

    前言 单例模式是创建模式中比较常见和常用的模式,在程序执行的整个生命周期只存在一个实例对象. 系列文章 python设计模式之单例模式(一) python设计模式之常用创建模式总结(二) python ...

  4. python设计模式之单例模式(二)

    上次我们简单了解了一下什么是单例模式,今天我们继续探究.上次的内容点这 python设计模式之单例模式(一) 上次们讨论的是GoF的单例设计模式,该模式是指:一个类有且只有一个对象.通常我们需要的是让 ...

  5. python设计模式之单例模式(转)

    设计模式之单例模式 单例设计模式是怎么来的?在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法 ...

  6. python_way,day8 面向对象【多态、成员--字段 方法 属性、成员修饰符、特殊成员、异常处理、设计模式之单例模式、模块:isinstance、issubclass】

    python_way day8 一.面向对象三大特性: 多态 二.面向对象中的成员 字段.方法属性 三.成员修饰符 四.特殊成员 __init__.__doc__.__call__.__setitem ...

  7. 文成小盆友python-num8 面向对象中的成员,成员修饰符,特殊成员,异常处理,设计模式之单例模式

    本节主要内容: 1.面向对象中的成员 2.成员修饰符 3.特殊成员 4.异常处理 5.设计模式之单例模式 一.面向对象中的成员(类的成员) 类的成员总共可以分为3大类,每类中有不同的分支. 1.总述, ...

  8. 由Python通过__new__实现单例模式,所想到的__new__和__init__方法的区别

    之前通过读书,了解到在Python中可以通过__new__方法来实现单例模式,代码一个示例如下,我就有了几个疑问,什么是单例模式?__new__方法是用来做什么的?用__new__方法实现的单例模式, ...

  9. Python与设计模式之创建型模式及实战

    用Python学习一下设计模式,如果很枯燥的话,就强行能使用的就用一下.设计模式参考Python与设计模式-途索 1. 单例模式 保证一个类仅有一个实例,并提供一个访问它的全局访问点. import ...

随机推荐

  1. C#编程语言与面向对象—— 多态

    多态编程的基本原理是: 使用基类或接口变量编程. 在多态编程中,基类一般都是抽象类,其中拥有一个或多个抽象方法,各个子类可以根据需要重写这些方法.或者使用接口,每个接口都规定了一个或多个抽象方法,实现 ...

  2. EL与Velocity基本语法总结:

    El(expression language): 基本语法点: $与{}搭配使用是常态取值 . 与[]的区别,后者可以取特殊值:- .等 支持一些基本的逻辑运算: && || > ...

  3. ubuntu安装opencv

    ubuntu版本是12.04 ,opencv的版本是2.4.9 其实官网有教程的,http://docs.opencv.org/doc/tutorials/introduction/linux_ins ...

  4. CSS实现垂直居中的5种方法

    利用 CSS 来实现对象的垂直居中有许多不同的方法,比较难的是选择那个正确的方法.我下面说明一下我看到的好的方法和怎么来创建一个好的居中网站. 使用 CSS 实现垂直居中并不容易.有些方法在一些浏览器 ...

  5. php获取post参数的几种方式 RPC 规定接收取值方式 $GLOBALS['HTTP_RAW_POST_DATA'];

    http://www.cnblogs.com/zhepama/p/4022606.html PHP默认识别的数据类型是application/x-www.form-urlencoded标准的数据类型. ...

  6. iOS_Quartz2D之涂鸦板

    响应者对象:继承了UIResponder的对象 触摸事件:一根或多根手指: 开始触摸: - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent ...

  7. Unity3D入门

    Unity3D是一款应用广泛的3D游戏引擎,本文主要介绍unity3D的简单应用,安装过程略过. 在游戏的整个开发过程中,游戏界面设计占据非常重要的地位.因为游戏启动后,第一个映入眼帘的就是整个游戏U ...

  8. 0x00linux32位汇编初入--前期准备

    0x00汇编初入--前期准备 一.汇编工具 在linux平台下常用的编译器为as,连接器为ld,使用的文本编辑器为vim,汇编语法为att 以下是一些工具: addr2line 把地址转换为文件名和行 ...

  9. 转载:bootstrap, boosting, bagging 几种方法的联系

    转:http://blog.csdn.net/jlei_apple/article/details/8168856 这两天在看关于boosting算法时,看到一篇不错的文章讲bootstrap, ja ...

  10. Java基础--serialVersionUID

    Java基础--serialVersionUID serialVersionUID作用: 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性.有两种生成方式: 一个是默认的1L,比 ...