Python设计模式——单例模式

本文探讨Python中的单例模式,通过两种方法实现:一是利用类属性确保只有一个实例;二是采用Borg惯用法实现共享属性。同时,文章提及了子类化单例模式的注意事项,并讨论了如何调整Borg类以更接近单例行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单例模式:保证某个类中只应该有一个实例。

这里我们将使用Python Cookbook的代码

方法一,继承Singleton类:

class Singleton(object):
    """一个Python风格的单例模式"""
    def __new__(cls, *args, **kwargs):
        if not hasattr(cls,'_inst'):
            cls._inst = super(Singleton, cls).__new__(cls, *args, **kwargs)
        return cls._inst

if __name__ == '__main__':
    class SingleSpam(Singleton):
        def __init__(self, s):
            self.s = s
        def __str__(self):
            return self.s

    s1 = SingleSpam('spam')
    s2 = SingleSpam('eggs')
    print id(s1), s1
    print id(s2), s2

通过__new__方法,将类的实例在创建的时候绑定到类属性_inst上。如果cls._instNone,说明类还未实例化,实例化并将实例绑定到cls._inst,以后每次实例化的时候都返回第一次实例化创建的实例。注意从Singleton派生子类的时候,不要重载__new__

关于单例的一个常见议题是子类化。Singleton类的编写方式意味着它的每个子类,包括直接子类或者间接子类,都能个分别获得一个独立的实例(这个问题是Cookbook原文,经过下面的实例得出这个结果应该是在Python之后的版本(现在用的是2.7,书中是2.4)有所改进,所以这段文字应该是错误的)。从字面意思上讲,似乎违背了单一实例的元组,不过这依赖于”单一”的具体含义:

class Foo(Singleton):pass
class Bar(Foo): pass
f = Foo()
b = Bar()
print f is b, isinstance(f, Foo), isinstance(b, Foo)
#True, True, True

方法二:用Borg惯用法来避免”单例”模式

class Borg(object):
    _shared_state={}
    def __new__(cls,*args,**kwargs):
        obj=super(Borg,cls).__new__(cls,*args,**kwargs)
        obj.__dict__=cls._shared_state
        return obj

重载子类的__new__(少数才需要),重载的是Borg.__new__

将所有实例的__dict__指向同一个字典,这样实例就共享相同的方法和属性。对任何实例的名字属性的设置,无论是在__init__中修改还是直接修改,所有的实例都会受到影响。不过实例的id是不同的。要保证类实例能共享属性,但不和子类共享,注意使用cls._shared_state,而不是Borg._shared_state

使用:

if __name__=='__main__':
    class Example(Borg):
        name = None
        def __init__(self, name=None):
            if name:
                self.name = name
        def __str__(self):
            return 'name->%s' % self.name
       a = Example('Lara')
    b = Example()
    print a, b
    c = Example('Join')
    print a, b, c
    b.name = 'Seven'
    print a, b, c

#结果
name->Lara name->Lara
name->Join name->Join name->Join
name->Seven name->Seven name->Seven

但这其中Example的实例id是不同的,既然我们没有定义特殊方法__eq____hash__,则每个实例都可以作为字典的独立的键。

    adict = {}
    j = 0
    for i in a, b, c:
        adict[i] = j
        j = j + 1
    for i in a, b ,c:
        print i, adict[i]

#结果
name->Seven 0
name->Seven 1
name->Seven 2

如果这种行为不是你想要的,可以为Borg类添加eqhash方法,使其更接近于单例模式的行为:

class Borg(object):
    _shared_state={}
    def __new__(cls,*args,**kwargs):
        obj=super(Borg,cls).__new__(cls,*args,**kwargs)
        obj.__dict__=cls._shared_state
        return obj
    def __hash__(self):
        return 1
    def __eq__(self,other):
        try:
            return self.__dict__ is other.__dict__
        except:
            return False
if __name__=='__main__':
    class Example(Borg):
        pass
    a=Example()
    b=Example()
    c=Example()
    adict={}
    j=0
    for i in a,b,c:
        adict[i]=j
        j+=1
    for i in a,b,c:
        print adict[i]

#结果
name->Seven 2
name->Seven 2
name->Seven 2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值