详解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二维码生成库qrcode安装和使用示例
Dec 16 Python
在Django框架中伪造捕捉到的URLconf值的方法
Jul 18 Python
python 写的一个爬虫程序源码
Feb 28 Python
Python和Perl绘制中国北京跑步地图的方法
Mar 03 Python
Python while 循环使用的简单实例
Jun 08 Python
Python实现的破解字符串找茬游戏算法示例
Sep 25 Python
python3 字符串/列表/元组(str/list/tuple)相互转换方法及join()函数的使用
Apr 03 Python
python实现串口通信的示例代码
Feb 10 Python
记一次django内存异常排查及解决方法
Aug 07 Python
python绘制雷达图实例讲解
Jan 03 Python
使用Python脚本对GiteePages进行一键部署的使用说明
May 27 Python
Python实现GIF动图以及视频卡通化详解
Dec 06 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编码更加好看利于阅读
2019/05/12 PHP
JQuery 表格操作(交替显示、拖动表格行、选择行等)
2009/07/29 Javascript
Jquery Autocomplete 结合asp.net使用要点
2010/10/29 Javascript
让你的博文自动带上缩址的实现代码,方便发到微博客上
2010/12/28 Javascript
JavaScript学习笔记记录我的旅程
2012/05/23 Javascript
JS 两日期相减,获得天数的小例子(兼容IE,FF)
2013/07/01 Javascript
JavaScript设置首页和收藏页面的小例子
2013/11/11 Javascript
js动态移动滚动条至底部示例代码
2014/04/24 Javascript
浅谈Javascript中匀速运动的停止条件
2014/12/19 Javascript
为什么JS中eval处理JSON数据要加括号
2015/04/13 Javascript
解决jQuery上传插件Uploadify出现Http Error 302错误的方法
2015/12/18 Javascript
JavaScript关于提高网站性能的几点建议(一)
2016/07/24 Javascript
jQuery实现一个简单的验证码功能
2017/06/26 jQuery
JavaScript函数、闭包、原型、面向对象学习笔记
2018/09/06 Javascript
vue实现同一个页面可以有多个router-view的方法
2018/09/20 Javascript
详解关于微信setData回调函数中的坑
2019/02/18 Javascript
详解ng-alain动态表单SF表单项设置必填和正则校验
2019/06/11 Javascript
python学习手册中的python多态示例代码
2014/01/21 Python
Python 查看文件的编码格式方法
2017/12/21 Python
PyQt5每天必学之弹出消息框
2018/04/19 Python
在spyder IPython console中,运行代码加入参数的实例
2020/04/20 Python
python实现按键精灵找色点击功能教程,使用pywin32和Pillow库
2020/06/04 Python
python属于软件吗
2020/06/18 Python
python 检测nginx服务邮件报警的脚本
2020/12/31 Python
python matplotlib工具栏源码探析二之添加、删除内置工具项的案例
2021/02/25 Python
护理职业生涯规划书
2014/01/24 职场文书
大学军训感言1000字
2014/02/25 职场文书
酒店保安领班职务说明书
2014/03/04 职场文书
高中生第一学年自我鉴定2015
2014/09/28 职场文书
篮球友谊赛通讯稿
2014/10/10 职场文书
财务工作个人总结
2015/02/27 职场文书
创业计划书之冷饮店
2019/09/27 职场文书
python实现三次密码验证的示例
2021/04/29 Python
java Nio使用NioSocket客户端与服务端交互实现方式
2021/06/15 Java/Android
一篇文章了解正则表达式的替换技巧
2022/02/24 Javascript
台式电脑蓝牙适配器怎么安装?台式电脑蓝牙适配器安装教程
2022/04/08 数码科技