详解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实现可将字符转换成大写的tcp服务器实例
Apr 29 Python
Python遍历指定文件及文件夹的方法
May 09 Python
python:socket传输大文件示例
Jan 18 Python
python中matplotlib的颜色及线条控制的示例
Mar 16 Python
Python实现的计算器功能示例
Apr 26 Python
Python排序算法之选择排序定义与用法示例
Apr 29 Python
python 拼接文件路径的方法
Oct 23 Python
详解Python 字符串相似性的几种度量方法
Aug 29 Python
python使用socket 先读取长度,在读取报文内容示例
Sep 26 Python
python tornado使用流生成图片的例子
Nov 18 Python
python实现程序重启和系统重启方式
Apr 16 Python
Python更换pip源方法过程解析
May 19 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
CodeIgniter生成静态页的方法
2016/05/17 PHP
Laravel 集成 Geetest验证码的方法
2018/05/14 PHP
深入学习微信网址链接解封的防封原理visit_type
2019/08/15 PHP
JavaScript触发器详解
2007/03/10 Javascript
JS option location 页面跳转实现代码
2008/12/27 Javascript
jQuery一步一步实现跨浏览器的可编辑表格,支持IE、Firefox、Safari、Chrome、Opera
2009/08/28 Javascript
js实现运动logo图片效果及运动元素对象sportBox使用方法
2012/12/25 Javascript
js动态添加onload、onresize、onscroll事件(另类方法)
2012/12/26 Javascript
如何使用jquery动态加载js,css文件实现代码
2013/04/03 Javascript
jquery实现简单的拖拽效果实例兼容所有主流浏览器
2013/06/21 Javascript
javascript转换静态图片,增加粒子动画效果
2015/05/28 Javascript
js+div实现文字滚动和图片切换效果代码
2015/08/27 Javascript
通过sails和阿里大于实现短信验证
2017/01/04 Javascript
react-router中的属性详解
2017/06/01 Javascript
Easyui和zTree两种方式分别实现树形下拉框
2017/08/04 Javascript
小程序实现带年月选取效果的日历
2018/06/27 Javascript
Python的面向对象思想分析
2015/01/14 Python
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)
2019/05/10 Python
Python二进制文件读取并转换为浮点数详解
2019/06/25 Python
在pycharm下设置自己的个性模版方法
2019/07/15 Python
python并发编程多进程之守护进程原理解析
2019/08/20 Python
使用Python进行中文繁简转换的实现代码
2019/10/18 Python
Django视图扩展类知识点详解
2019/10/25 Python
Python基于yaml文件配置logging日志过程解析
2020/06/23 Python
scrapy头部修改的方法详解
2020/12/06 Python
绝对令人的惊叹的CSS3折叠效果(3D效果)整理
2012/12/30 HTML / CSS
Expedia印度:您的一站式在线旅游网站
2017/08/24 全球购物
美国保健品专家:Life Extension
2018/05/04 全球购物
全球性的女装店:storets
2019/06/12 全球购物
马来西亚在线购物:POPLOOK.com
2019/12/09 全球购物
男方父母证婚词
2014/01/12 职场文书
旺仔牛奶广告词
2014/03/20 职场文书
代理协议书范本
2014/04/22 职场文书
小学红领巾广播稿(3篇)
2014/09/13 职场文书
实习指导老师意见
2015/06/04 职场文书
Windows环境下实现批量执行Sql文件
2021/10/05 SQL Server