详解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实现删除Android工程中的冗余字符串
Jan 19 Python
轻松实现python搭建微信公众平台
Feb 16 Python
Python自定义类的数组排序实现代码
Aug 28 Python
numpy中的ndarray方法和属性详解
May 27 Python
Python替换月份为英文缩写的实现方法
Jul 15 Python
对Python 中矩阵或者数组相减的法则详解
Aug 26 Python
tensorflow如何批量读取图片
Aug 29 Python
windows 10 设定计划任务自动执行 python 脚本的方法
Sep 11 Python
在Python中使用filter去除列表中值为假及空字符串的例子
Nov 18 Python
python颜色随机生成器的实例代码
Jan 10 Python
Python脚本导出为exe程序的方法
Mar 25 Python
python读取hdfs并返回dataframe教程
Jun 05 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
PHP实现通过正则表达式替换回调的内容标签
2015/06/15 PHP
PHP如何通过传引用的思想实现无限分类(代码简单)
2015/10/13 PHP
Zend Framework教程之请求对象的封装Zend_Controller_Request实例详解
2016/03/07 PHP
PHP验证终端类型是否为手机的简单实例
2017/02/07 PHP
phpinfo的知识点总结
2019/10/10 PHP
img标签中onerror用法
2009/08/13 Javascript
js函数调用常用方法详解
2012/12/03 Javascript
jQuery操作input值的各种方法总结
2013/11/21 Javascript
javascript使用正则表达式检测IP地址
2014/12/03 Javascript
windows8.1+iis8.5下安装node.js开发环境
2014/12/12 Javascript
node.js中的buffer.toJSON方法使用说明
2014/12/14 Javascript
jQuery实现精美的多级下拉菜单特效
2015/03/14 Javascript
JS实现带关闭功能的阿里妈妈网站顶部滑出banner工具条代码
2015/09/17 Javascript
codeMirror插件使用讲解
2017/01/16 Javascript
Node.js中流(stream)的使用方法示例
2017/07/16 Javascript
vuex学习之Actions的用法详解
2017/08/29 Javascript
JS求1到任意数之间的所有质数的方法详解
2019/05/20 Javascript
Vue.js实现tab切换效果
2019/07/24 Javascript
Node.JS如何实现JWT原理
2020/09/18 Javascript
微信小程序自定义底部弹出框功能
2020/11/18 Javascript
[41:37]DOTA2北京网鱼队选拔赛——冲击职业之路
2015/04/13 DOTA
Python基于Tkinter的HelloWorld入门实例
2015/06/17 Python
Python实现运行其他程序的四种方式实例分析
2017/08/17 Python
pandas去重复行并分类汇总的实现方法
2019/01/29 Python
python将邻接矩阵输出成图的实现
2019/11/21 Python
Python中bisect的用法及示例详解
2020/07/20 Python
用CSS3实现背景渐变的方法
2015/07/14 HTML / CSS
英国排名第一的LED灯泡网站:LED Bulbs
2019/09/03 全球购物
澳大利亚领先的时尚内衣零售商:Bras N Things
2020/07/28 全球购物
计算机数据库专业职业生涯规划书
2014/02/08 职场文书
党的群众路线教育实践活动宣传方案
2014/02/23 职场文书
鼓舞士气的口号
2014/06/16 职场文书
2014党员学习《反腐倡廉警示教育读本》思想汇报
2014/09/13 职场文书
学校实习推荐信
2015/03/27 职场文书
spring boot项目application.properties文件存放及使用介绍
2021/06/30 Java/Android
Python线程池与GIL全局锁实现抽奖小案例
2022/04/13 Python