python单例设计模式

我们先来看看单例模式。单例模式是设计模式中逻辑最简单,最容易理解的一个模式,简单到只需要一句话就可以理解,即“保证只有一个对象实例的模式”。问题的关键在于实现起来并没有想象的那么简单。不过我们还是先来讨论下为什么需要这个模式吧。
为什么
我们首先来看看单例模式的使用场景,然后再来分析为什么需要单例模式。

Python的logger就是一个单例模式,用以日志记录
Windows的资源管理器是一个单例模式
线程池,数据库连接池等资源池一般也用单例模式
网站计数器

从这些使用场景我们可以总结下什么情况下需要单例模式:

当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;
当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。

当然所有使用单例模式的前提是我们的确用一个实例就可以搞定要解决的问题,而不需要多个实例,如果每个实例都需要维护自己的状态,这种情况下单例模式肯定是不适用的。
接下来看看如何使用Python来实现一个单例模式。
是什么
最开始的想法很简单,实现如下:
class Singleton(object):
instance = None
def new(cls, *args, **kwargs): # 这里不能使用__init
,因为__init__是在instance已经生成以后才去调用的
if cls.__instance is None:
cls.__instance = super(Singleton, cls).new(cls, *args, **kwargs)
return cls.__instance

s1 = Singleton()
s2 = Singleton()
print s1
print s2

打印结果如下:
<main.Singleton object at 0x7f3580dbe110>
<main.Singleton object at 0x7f3580dbe110>

可以看出两次创建对象,结果返回的是同一个对象实例,我们再让我们的例子更接近真实的使用场景来看看
class Singleton(object):
__instance = None

def __new__(cls, *args, **kwargs):
    if cls.__instance is None:
        cls.__instance = super(
            Singleton, cls).__new__(cls, *args, **kwargs)
    return cls.__instance

def __init__(self, status_number):
    self.status_number = status_number

s1 = Singleton(2)
s2 = Singleton(5)
print s1
print s2

print s1.status_number
print s2.status_number

这里我们使用了_init_方法,下面是打印结果,可以看出确实是只有一个实例,共享了实例的变量
<main.Singleton object at 0x7f5116865490>
<main.Singleton object at 0x7f5116865490>
5
5

不过这个例子中有一个问题我们没有解决,那就是多线程的问题,当有多个线程同时去初始化对象时,就很可能同时判断__instance is None,从而进入初始化instance的代码中。所以为了解决这个问题,我们必须通过同步锁来解决这个问题。以下例子来自xiaorui
import threading
try:
from synchronize import make_synchronized
except ImportError:
def make_synchronized(func):
import threading
func.lock = threading.Lock()

    def synced_func(*args, **kws):
        with func.__lock__:
            return func(*args, **kws)

    return synced_func

class Singleton(object):
instance = None

@make_synchronized
def __new__(cls, *args, **kwargs):
    if cls.instance is None:
        cls.instance = object.__new__(cls, *args, **kwargs)
    return cls.instance

def __init__(self):
    self.blog = "xiaorui.cc"

def go(self):
    pass

def worker():
e = Singleton()
print id(e)
e.go()

def test():
e1 = Singleton()
e2 = Singleton()
e1.blog = 123
print e1.blog
print e2.blog
print id(e1)
print id(e2)

if name == “main”:
test()
task = []
for one in range(30):
t = threading.Thread(target=worker)
task.append(t)

for one in task:
    one.start()

for one in task:
    one.join()

至此我们的单例模式实现代码已经接近完美了,不过我们是否可以更简单地使用单例模式呢?答案是有的,接下来就看看如何更简单地使用单例模式。
怎么用
在Python的官方网站给了两个例子是用装饰符来修饰类,从而使得类变成了单例模式,使得我们可以通过更加简单的方式去实现单例模式
例子:(这里只给出一个例子,因为更简单,另外一个大家可以看官网Singleton
def singleton(cls):
instance = cls()
instance.call = lambda: instance
return instance

Sample use

@singleton
class Highlander:
x = 100
# Of course you can have any attributes or methods you like.

Highlander() is Highlander() is Highlander #=> True
id(Highlander()) == id(Highlander) #=> True
Highlander().x == Highlander.x == 100 #=> True
Highlander.x = 50
Highlander().x == Highlander.x == 50 #=> True

这里简单解释下:

在定义class Highlander的时候已经执行完所有singleton装饰器中的代码,得到了一个instance,所以这之后所有对Highlander的调用实际上是在调用instance的_call_ 方法。
我们通过lambda函数定义了_call_方法让它始终返回instance,因此Highlander()和Highlander都返回instance
同时由于在类定义代码执行时就已经创建了instance,所以后续不论是多线程还是单线程,在调用Highlander时都是在调用instance的_call_方法,也就无需同步了。
最后我想说的是这种方法简直碉堡了~~~
附上我用于多线程的测试代码

import threading

def singleton(cls):
instance = cls()
instance.call = lambda: instance
return instance

@singleton
class Highlander:
x = 100
# Of course you can have any attributes or methods you like.

def worker():
hl = Highlander()
hl.x += 1
print hl
print hl.x

def main():
threads = []
for _ in xrange(50):
t = threading.Thread(target=worker)
threads.append(t)

for t in threads:
    t.start()

for t in threads:
    t.join()

if name == ‘main’:
main()

这里的代码有一点小问题,就是在打印的时候有可能x属性已经被别的线程+1了,所以有可能导致同一个数打印多次,而有的数没有打印,但是不影响最终x属性的结果,所以当所有线程结束之后,属性x最终的值是可以保证正确的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值