详解python单例模式与metaclass


Posted in Python onJanuary 15, 2016

单例模式的实现方式

将类实例绑定到类变量上

class Singleton(object):
  _instance = None

  def __new__(cls, *args):
    if not isinstance(cls._instance, cls):
      cls._instance = super(Singleton, cls).__new__(cls, *args)
    return cls._instance

但是子类在继承后可以重写__new__以失去单例特性

class D(Singleton):

  def __new__(cls, *args):
    return super(D, cls).__new__(cls, *args)

使用装饰器实现

def singleton(_cls):
  inst = {}

  def getinstance(*args, **kwargs):
    if _cls not in inst:
      inst[_cls] = _cls(*args, **kwargs)
    return inst[_cls]
  return getinstance

@singleton
class MyClass(object):
  pass

问题是这样装饰以后返回的不是类而是函数,当然你可以singleton里定义一个类来解决问题,但这样就显得很麻烦了

使用__metaclass__,这个方式最推荐

class Singleton(type):
  _inst = {}
  
  def __call__(cls, *args, **kwargs):
    if cls not in cls._inst:
      cls._inst[cls] = super(Singleton, cls).__call__(*args)
    return cls._inst[cls]


class MyClass(object):
  __metaclass__ = Singleton

metaclass

元类就是用来创建类的东西,可以简单把元类称为“类工厂”,类是元类的实例。type就是Python的内建元类,type也是自己的元类,任何一个类

>>> type(MyClass)
type
>>> type(type)
type

python在创建类MyClass的过程中,会在类的定义中寻找__metaclass__,如果存在则用其创建类MyClass,否则使用内建的type来创建类。对于类有继承的情况,如果当前类没有找到,会继续在父类中寻找__metaclass__,直到所有父类中都没有找到才使用type创建类。
如果模块里有__metaclass__的全局变量的话,其中的类都将以其为元类,亲自试了,没这个作用,无任何影响

查看type的定义,

type(object) -> the object's type
type(name, bases, dict) -> a new type

所以利用type定义一个类的元类,可以用函数返回一个上面第二种定义的对象,也可以继承type并重写其中的方法。

直接使用type生成的对象作为元类,函数作用是使属性变为大写

def update_(name, bases, dct):
  attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
  uppercase_attr = {name.upper(): value for name, value in attrs}
  return type(name, bases, uppercase_attr)


class Singleton(object):
  __metaclass__ = update_
  abc = 2

d = Singleton()
print d.ABC
# 2

上一节中,单例模式元类实现用的是类继承方式,而对于第一种__new__的方式,本质上调用的是type.__new__,不过使用super能使继承更清晰一些并避免一些问题

这里简单说明一下,__new__是在__init__前调用的方法,会创建对象并返回,而__init__则是用传入的参数将对象初始化。看一下type中这两者以及__call__的实现

def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__
    """
    type(object) -> the object's type
    type(name, bases, dict) -> a new type
    # (copied from class doc)
    """
    pass

@staticmethod # known case of __new__
def __new__(S, *more): # real signature unknown; restored from __doc__
  """ T.__new__(S, ...) -> a new object with type S, a subtype of T """
  pass

def __call__(self, *more): # real signature unknown; restored from __doc__
  """ x.__call__(...) <==> x(...) """
  pass

前面提到类相当于元类的实例化,再联系创建单例模式时使用的函数,用的是__call__,其实用三种magic method中任何一种都是可以的,来看一下使用元类时各方法的调用情况

class Basic(type):
  def __new__(cls, name, bases, newattrs):
    print "new: %r %r %r %r" % (cls, name, bases, newattrs)
    return super(Basic, cls).__new__(cls, name, bases, newattrs)

  def __call__(self, *args):
    print "call: %r %r" % (self, args)
    return super(Basic, self).__call__(*args)

  def __init__(cls, name, bases, newattrs):
    print "init: %r %r %r %r" % (cls, name, bases, newattrs)
    super(Basic, cls).__init__(name, bases, dict)


class Foo:
  __metaclass__ = Basic

  def __init__(self, *args, **kw):
    print "init: %r %r %r" % (self, args, kw)

a = Foo('a')
b = Foo('b')

结果

new: <class '__main__.Basic'> 'Foo' () {'__module__': '__main__', '__metaclass__': <class '__main__.Basic'>, '__init__': <function init at 0x106fd5320>}
init: <class '__main__.Foo'> 'Foo' () {'__module__': '__main__', '__metaclass__': <class '__main__.Basic'>, '__init__': <function init at 0x106fd5320>}
call: <class '__main__.Foo'> ('a',)
init: <__main__.Foo object at 0x106fee990> ('a',) {}
call: <class '__main__.Foo'> ('b',)
init: <__main__.Foo object at 0x106feea50> ('b',) {}

元类的__init__和__new__只在创建类Foo调用了一次,而创建Foo的实例时,每次都会调用元类的__call__方法

以上就是本文的全部内容,对python单例模式与metaclass进行了描述,希望对大家的学习有所帮助。

Python 相关文章推荐
Python中shutil模块的学习笔记教程
Apr 04 Python
新手如何快速入门Python(菜鸟必看篇)
Jun 10 Python
Python md5与sha1加密算法用法分析
Jul 14 Python
Python中类的初始化特殊方法
Dec 01 Python
python 实现selenium断言和验证的方法
Feb 13 Python
python批量修改文件夹及其子文件夹下的文件内容
Mar 15 Python
pyqt5 禁止窗口最大化和禁止窗口拉伸的方法
Jun 18 Python
Python assert语句的简单使用示例
Jul 28 Python
Python 私有化操作实例分析
Nov 21 Python
如何实现在jupyter notebook中播放视频(不停地展示图片)
Apr 23 Python
python 操作excel表格的方法
Dec 05 Python
详解Python自动化之文件自动化处理
Jun 21 Python
理解python正则表达式
Jan 15 #Python
Python工程师面试题 与Python Web相关
Jan 14 #Python
Python工程师面试题 与Python基础语法相关
Jan 14 #Python
5种Python单例模式的实现方式
Jan 14 #Python
Python2.x与Python3.x的区别
Jan 14 #Python
python Django模板的使用方法
Jan 14 #Python
Python数据类型学习笔记
Jan 13 #Python
You might like
德生PL550的电路分析
2021/03/02 无线电
Zend Framework校验器Zend_Validate用法详解
2016/12/09 PHP
ThinkPHP框架分布式数据库连接方法详解
2017/03/14 PHP
php实现的PDO异常处理操作分析
2018/12/27 PHP
抛弃Nginx使用nodejs做反向代理服务器
2014/07/17 NodeJs
jQuery实现下拉框选择图片功能实例
2015/08/08 Javascript
基于javascript实现动态显示当前系统时间
2016/01/28 Javascript
学习使用jquery iScroll.js移动端滚动条插件
2020/03/24 Javascript
深入剖析JavaScript:Object类型
2016/05/10 Javascript
Bootstrap模态框调用功能实现方法
2016/09/19 Javascript
JS计算两个时间相差分钟数的方法示例
2018/01/10 Javascript
js中apply和Math.max()函数的问题及区别介绍
2018/03/27 Javascript
JS使用Date对象实时显示当前系统时间简单示例
2018/08/23 Javascript
javascript绘制简单钟表效果
2020/04/07 Javascript
解决Antd Table表头加Icon和气泡提示的坑
2020/11/17 Javascript
[01:33:14]LGD vs VP Supermajor 败者组决赛 BO3 第二场 6.10
2018/07/04 DOTA
[02:04]2020年夜魇暗潮预告片
2020/10/30 DOTA
详细讲解用Python发送SMTP邮件的教程
2015/04/29 Python
Python获取CPU、内存使用率以及网络使用状态代码
2018/02/08 Python
Python for循环生成列表的实例
2018/06/15 Python
详解Python的hasattr() getattr() setattr() 函数使用方法
2018/07/09 Python
Python正则表达式实现简易计算器功能示例
2019/05/07 Python
Python 多线程其他属性以及继承Thread类详解
2019/08/28 Python
Python 函数绘图及函数图像微分与积分
2019/11/20 Python
python将字典内容写入json文件的实例代码
2020/08/12 Python
Python xmltodict模块安装及代码实例
2020/10/05 Python
Selenium执行完毕未关闭chromedriver/geckodriver进程的解决办法(java版+python版)
2020/12/07 Python
python中的unittest框架实例详解
2021/02/05 Python
POP文化和音乐灵感的时尚:Hot Topic
2019/06/19 全球购物
给市场的环保建议书
2014/05/14 职场文书
2014年教师节演讲稿范文
2014/09/10 职场文书
公司职员入党自传书
2015/06/26 职场文书
深入探讨opencv图像矫正算法实战
2021/05/21 Python
Java常用工具类汇总 附示例代码
2021/06/26 Java/Android
python调用ffmpeg命令行工具便捷操作视频示例实现过程
2021/11/01 Python
python游戏开发Pygame框架
2022/04/22 Python