详解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抓取豆瓣图片并自动保存示例学习
Jan 10 Python
Python的Django框架使用入门指引
Apr 15 Python
Python 专题二 条件语句和循环语句的基础知识
Mar 19 Python
基于Python对象引用、可变性和垃圾回收详解
Aug 21 Python
人机交互程序 python实现人机对话
Nov 14 Python
pandas Dataframe行列读取的实例
Jun 08 Python
Scrapy-Redis结合POST请求获取数据的方法示例
May 07 Python
Linux下升级安装python3.8并配置pip及yum的教程
Jan 02 Python
Pymysql实现往表中插入数据过程解析
Jun 02 Python
pytorch随机采样操作SubsetRandomSampler()
Jul 07 Python
Python入门之基础语法详解
May 11 Python
Python中的xlrd模块使用整理
Jun 15 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缓存机制Output Control详解
2014/07/14 PHP
php实现获取局域网所有用户的电脑IP和主机名、及mac地址完整实例
2014/07/18 PHP
ThinkPHP查询返回简单字段数组的方法
2014/08/25 PHP
PHP file_get_contents函数读取远程数据超时的解决方法
2015/05/13 PHP
摘自启点的main.js
2008/04/20 Javascript
IE浏览器兼容Firefox的JS脚本的代码
2008/10/23 Javascript
Javascript 实现TreeView CheckBox全选效果
2010/01/11 Javascript
一个封装js代码-----展开收起效果示例
2013/07/03 Javascript
Javascript基础教程之while语句
2015/01/18 Javascript
JavaScript中解析JSON数据的三种方法
2015/07/03 Javascript
jquery中checkbox使用方法简单实例演示
2015/11/24 Javascript
整理Javascript事件响应学习笔记
2015/12/02 Javascript
利用Jquery队列实现根据输入数量显示的动画
2016/09/01 Javascript
JQuery页面随滚动条动态加载效果的简单实现(推荐)
2017/02/08 Javascript
JS实现的数字格式化功能示例
2017/02/10 Javascript
用原生JS实现简单的多选框功能
2017/06/12 Javascript
js链表操作(实例讲解)
2017/08/29 Javascript
微信小程序自定义toast的实现代码
2018/11/16 Javascript
JS实现吸顶特效
2020/01/08 Javascript
Vue设置长时间未操作登录自动到期返回登录页
2020/01/22 Javascript
vue 检测用户上传图片宽高的方法
2020/02/06 Javascript
Vue中keep-alive 实现后退不刷新并保持滚动位置
2020/03/17 Javascript
解决ant design vue中树形控件defaultExpandAll设置无效的问题
2020/10/26 Javascript
ptyhon实现sitemap生成示例
2014/03/30 Python
利用Python暴力破解zip文件口令的方法详解
2017/12/21 Python
pycharm运行和调试不显示结果的解决方法
2018/11/30 Python
python进行文件对比的方法
2018/12/24 Python
Python closure闭包解释及其注意点详解
2019/08/28 Python
Python 解码Base64 得到码流格式文本实例
2020/01/09 Python
Python itertools.product方法代码实例
2020/03/27 Python
python输入一个水仙花数(三位数) 输出百位十位个位实例
2020/05/03 Python
高级Java程序员面试要点
2013/08/02 面试题
应聘自荐书
2013/10/08 职场文书
开业庆典邀请函
2014/01/08 职场文书
历史专业大学生职业生涯规划书
2014/03/13 职场文书
自动在Windows中运行Python脚本并定时触发功能实现
2021/09/04 Python