详解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备份文件的脚本
Aug 11 Python
Python中的多行注释文档编写风格汇总
Jun 16 Python
Python中实现最小二乘法思路及实现代码
Jan 04 Python
Django--权限Permissions的例子
Aug 28 Python
python3获取url文件大小示例代码
Sep 18 Python
Python matplotlib画曲线例题解析
Feb 07 Python
python用WxPython库实现无边框窗体和透明窗体实现方法详解
Feb 21 Python
python模拟点击网页按钮实现方法
Feb 25 Python
Python telnet登陆功能实现代码
Apr 16 Python
python爬虫beautifulsoup解析html方法
Dec 07 Python
Pyqt5将多个类组合在一个界面显示的完整示例
Sep 04 Python
python中出现invalid syntax报错的几种原因分析
Feb 12 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
建站常用13种PHP开源CMS比较
2009/08/23 PHP
[原创]PHP简单开启curl的方法(测试可行)
2016/01/11 PHP
js文件中调用js的实现方法小结
2009/10/23 Javascript
jquery validator 插件增加日期比较方法
2010/02/21 Javascript
jQuery+ajax实现顶一下,踩一下效果
2010/07/17 Javascript
JS判断字符串变量是否含有某个字串的实现方法
2016/06/03 Javascript
D3.js实现柱状图的方法详解
2016/09/21 Javascript
深入理解JavaScript定时机制
2016/10/27 Javascript
全新打包工具parcel零配置vue开发脚手架
2018/01/11 Javascript
详解刷新页面vuex数据不消失和不跳转页面的解决
2018/01/30 Javascript
Node.js HTTP服务器中的文件、图片上传的方法
2019/09/23 Javascript
实例讲解JavaScript 计时事件
2020/07/04 Javascript
Python对文件操作知识汇总
2016/05/15 Python
python WindowsError的错误代码详解
2017/07/23 Python
基于python3实现socket文件传输和校验
2018/07/28 Python
python买卖股票的最佳时机(基于贪心/蛮力算法)
2019/07/05 Python
python爬取盘搜的有效链接实现代码
2019/07/20 Python
Python使用selenium + headless chrome获取网页内容的方法示例
2019/10/16 Python
python+selenium+PhantomJS抓取网页动态加载内容
2020/02/25 Python
python实现可下载音乐的音乐播放器
2020/02/25 Python
Python matplotlib图例放在外侧保存时显示不完整问题解决
2020/07/28 Python
Python爬虫爬取糗事百科段子实例分享
2020/07/31 Python
Python的3种运行方式:命令行窗口、Python解释器、IDLE的实现
2020/10/10 Python
用60行代码实现Python自动抢微信红包
2021/02/04 Python
解决TensorFlow训练模型及保存数量限制的问题
2021/03/03 Python
CSS3教程(5):网页背景图片
2009/04/02 HTML / CSS
麦当劳辞职信范文
2014/01/18 职场文书
《陶罐和铁罐》教学反思
2014/02/19 职场文书
cf收人广告词大全
2014/03/14 职场文书
2014年学雷锋活动总结
2014/06/26 职场文书
批评与自我批评范文
2014/10/15 职场文书
2015元旦晚会主持词(开场白+结束语)
2014/12/14 职场文书
健康证明
2015/06/19 职场文书
缅怀先烈主题班会
2015/08/14 职场文书
七个Python必备的GUI库
2021/04/27 Python
科学家研发出新型速效酶,可在 24 小时内降解塑料制品
2022/04/29 数码科技