文档章节

Python实现单例模式

alazyer
 alazyer
发布于 2017/05/25 11:06
字数 1015
阅读 28
收藏 0

定义

An implementation of the singleton pattern must:

  1. ensure that only one instance of the singleton class ever exists; and
  2. provide global access to that instance.

单例模式就是保证类在整个使用过程中有且只有一个实例.

Python原理

要想保证类的使用过程中有且只有一个实例, 就需要对Python中类的实例化, 以及实例化后的使用过程有所了解. 这个过程主要涉及到3个方法:

  1. __new__
  2. __init__
  3. __call__

这3个方法的调用过程是: 首先调用__new__, 来生成新的实例; 其次, 调用__init__对新实例进行相关设置; 最后, 如果类中实现了__call__方法的话, 那么类的实例也是一个可调用对象, 调用实例时instance()其实就是调用实例所属的类类的__call__方法.

清楚了3个方法的调用时机, 再结合单例模式的定义就不难实现Python版本的单例模式了.

Python实现

Python中实现单例模式主要有一下几种方式:

  1. __call__
  2. __new__

__call__

在python中一切都是对象, 类其实也是一个对象, 一个类是其元类(MetaClass)的实例(定义类的时候其实就会调用元类的__init__方法).

这样理解过来, 类的实例初始化实际上就是通过调用了类的元类的__call__方法实现的.

# coding: utf-8
"""
代码摘在mininet源代码
"""

class Singleton( type ):
    """Singleton pattern from Wikipedia
       See http://en.wikipedia.org/wiki/Singleton_Pattern
       Intended to be used as a __metaclass_ param, as shown for the class
       below."""

    def __new__( cls, *args, **kw ):
        return super( Singleton, cls ).__new__( cls, *args, **kw )
    
    def __init__( cls, name, bases, dict_ ):
        super( Singleton, cls ).__init__( name, bases, dict_ )
        cls.instance = None

    def __call__( cls, *args, **kw ):
        if cls.instance is None:
            cls.instance = super( Singleton, cls ).__call__( *args, **kw )
        return cls.instance


class MininetLogger( Logger, object ):
    """Mininet-specific logger
       Enable each mininet .py file to with one import:
       from mininet.log import [lg, info, error]
       ...get a default logger that doesn't require one newline per logging
       call.
       Inherit from object to ensure that we have at least one new-style base
       class, and can then use the __metaclass__ directive, to prevent this
       error:
       TypeError: Error when calling the metaclass bases
       a new-style class can't have only classic bases
       If Python2.5/logging/__init__.py defined Filterer as a new-style class,
       via Filterer( object ): rather than Filterer, we wouldn't need this.
       Use singleton pattern to ensure only one logger is ever created."""

    __metaclass__ = Singleton

    def __init__( self ):

        Logger.__init__( self, "mininet" )

        # create console handler
        ch = StreamHandlerNoNewline()
        # create formatter
        formatter = logging.Formatter( LOGMSGFORMAT )
        # add formatter to ch
        ch.setFormatter( formatter )
        # add ch to lg
        self.addHandler( ch )

        self.setLogLevel()

    def setLogLevel( self, levelname=None ):
        """Setup loglevel.
           Convenience function to support lowercase names.
           levelName: level name from LEVELS"""
        level = LOGLEVELDEFAULT
        if levelname is not None:
            if levelname not in LEVELS:
                raise Exception( 'unknown levelname seen in setLogLevel' )
            else:
                level = LEVELS.get( levelname, level )

        self.setLevel( level )
        self.handlers[ 0 ].setLevel( level )

    # pylint: disable=method-hidden
    # "An attribute inherited from mininet.log hide this method" (sic)
    # Not sure why this is occurring - this function definitely gets called.

    # See /usr/lib/python2.5/logging/__init__.py; modified from warning()
    def output( self, msg, *args, **kwargs ):
        """Log 'msg % args' with severity 'OUTPUT'.
           To pass exception information, use the keyword argument exc_info
           with a true value, e.g.
           logger.warning("Houston, we have a %s", "cli output", exc_info=1)
        """
        if self.manager.disable >= OUTPUT:
            return
        if self.isEnabledFor( OUTPUT ):
            self._log( OUTPUT, msg, args, kwargs )

    # pylint: enable=method-hidden

每次实例化MininetLogger时, 其实调用了Singleton的__call__方法, 这个可以在Singleton和MininetLogger类中相关方法添加打印日志进行验证.

当然Singleton还有其他的定义方式:

# https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python
class Singleton(type):
    _instances = {}
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

但是这种方式在多个类(A, B)同时指向Singleton为__metaclass__的话, 当实例化A后, 可以通过B._instances看到到A的instance.

__new__

每次实例化一个类的新实例的时候其实会调用类的__new__方法, 这样就可以通过在类的__new__方法中进行唯一实例的控制.

# https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not isinstance(cls._instance, cls):
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance
        
class A(Singleton):
    pass
    
id(A()) == id(A())
# True

总结

单例模式就是保证在整个程序使用过程中无论实例化(class())多少次, 在内存中只保存一个实例. 在Python中通过利用__new____call__两个魔法函数就可以通过在类或者类的元类中定义这两个方法实现单例模式.

© 著作权归作者所有

alazyer
粉丝 5
博文 68
码字总数 35105
作品 0
程序员
私信 提问
python 与设计模式 ——工厂与单例

python 与设计模式 源码地址:[http://git.oschina.net/duoduo3_69/python_design_pattern][1] git checkout v001(这个版本与此篇博客相符) zarkpy里面运用了很多设计模式,以前一直很费解p...

duoduo3_69
2013/11/27
0
0
《 Head First 》学习笔记:策略模式 (python实现)

学习<head first>,书中是用java 来实现,我用python比较多,所以在这里记下 用python实现的方法 。 书中策略模式的定义 : 策略模式 : 策略模式定义了算法族,分别封装起来,让它们之间可以...

Jbryan
2013/06/13
0
0
写代码也有“套路”-谈谈设计模式

编程教室开了这么久,已经有很多人从完全零基础的小白成为了会写代码的菜鸟程序员,能够自己独立开发程序。不过到此阶段,常常会遇到瓶颈,感觉功能可以实现,但代码看起来有些别扭: 代码中...

crossin
02/22
0
0
每天一个设计模式之订阅-发布模式

博主按:《每天一个设计模式》旨在初步领会设计模式的精髓,目前采用(靠这吃饭)和(纯粹喜欢)两种语言实现。诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :)...

godbmw
2018/12/10
0
0
Python新式类 new init 单例模式与作用域(四)

1 新式类与旧式类 新式类拥有经典类的全部特性之外,还有一些新的特性,比如 发生变化,新增了静态方法,python3目前都采用新式类,新式类是广度优先,旧式类是深度优先 (2)类的方法 静态方法 类方...

善良小郎君
2018/06/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Android双向绑定原理简述

Android双向绑定原理简述 双向绑定涉及两个部分,即将业务状态的变化传递给UI,以及将用户输入信息传递给业务模型。 首先我们来看业务状态是如何传递给UI的。开启dataBinding后,编译器为布局...

tommwq
今天
2
0
Spring系列教程八: Spring实现事务的两种方式

一、 Spring事务概念: 事务是一系列的动作,它们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,那么事务就会回滚到最开始的状态,仿佛什么都没发生过一样。...

我叫小糖主
今天
6
0
CentOS 的基本使用

1. 使用 sudo 命令, 可以以 root 身份执行命令, 必须要在 /etc/sudoers 中定义普通用户 2. 设置 阿里云 yum 镜像, 参考 https://opsx.alibaba.com/mirror # 备份mv /etc/yum.repos.d/CentO...

北漂的我
昨天
3
0
Proxmox VE技巧 移除PVE “没有有效订阅” 的弹窗提示

登陆的时候提示没有有效的订阅You do not have a valid subscription for this server. Please visit www.proxmox.com to get a list of available options. 用的是免费版的,所以每次都提示......

以谁为师
昨天
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部