详解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采用socket模拟TCP通讯的实现方法
Nov 19 Python
Python函数式编程指南(四):生成器详解
Jun 24 Python
简析Python的闭包和装饰器
Feb 26 Python
windows下Virtualenvwrapper安装教程
Dec 13 Python
Python unittest 简单实现参数化的方法
Nov 30 Python
pytorch 转换矩阵的维数位置方法
Dec 08 Python
Python按钮的响应事件详解
Mar 04 Python
Python any()函数的使用方法
Oct 28 Python
python新手学习可变和不可变对象
Jun 11 Python
Python基础教程(一)——Windows搭建开发Python开发环境
Jul 20 Python
利用Python如何制作贪吃蛇及AI版贪吃蛇详解
Aug 24 Python
pytorch加载语音类自定义数据集的方法教程
Nov 10 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
人族 Terran 魔法与科技
2020/03/14 星际争霸
PHP上传文件时文件过大$_FILES为空的解决方法
2013/11/26 PHP
ThinkPHP静态缓存简单配置和使用方法详解
2016/03/23 PHP
Js中sort()方法的用法
2006/11/04 Javascript
jquery获得页面元素的坐标值实现思路及代码
2013/04/15 Javascript
js实现3D图片逐张轮播幻灯片特效代码分享
2015/09/09 Javascript
浅谈javascript中的call、apply、bind
2016/03/06 Javascript
jQuery UI Bootstrap是什么?
2016/06/17 Javascript
利用原生js和jQuery实现单选框的勾选和取消操作的方法
2016/09/04 Javascript
JS使用面向对象技术实现的tab选项卡效果示例
2017/02/28 Javascript
Js判断H5上下滑动方向及滑动到顶部和底部判断的示例代码
2017/11/15 Javascript
vue服务端渲染页面缓存和组件缓存的实例详解
2018/09/18 Javascript
微信小程序自定义导航栏
2018/12/31 Javascript
js计算两个时间差 天 时 分 秒 毫秒的代码
2019/05/21 Javascript
微信小程序实现比较功能的方法汇总(五种方法)
2020/03/07 Javascript
Vue项目打包压缩的实现(让页面更快响应)
2020/03/10 Javascript
使用React-Router实现前端路由鉴权的示例代码
2020/07/26 Javascript
[02:32]DOTA2完美大师赛场馆静安体育中心观赛全攻略
2017/11/08 DOTA
Python基本数据结构与用法详解【列表、元组、集合、字典】
2019/03/23 Python
python 返回一个列表中第二大的数方法
2019/07/09 Python
Python 操作 ElasticSearch的完整代码
2019/08/04 Python
flask实现验证码并验证功能
2019/12/05 Python
python GUI库图形界面开发之PyQt5信号与槽机制、自定义信号基础介绍
2020/02/25 Python
Javascript 高级手势使用介绍
2013/04/21 HTML / CSS
trivago美国:全球最大的酒店价格比较网站
2018/01/18 全球购物
施华洛世奇美国官网:SWAROVSKI美国
2018/02/08 全球购物
Rhone官方网站:男士运动服装、健身服装和高级运动服
2019/05/01 全球购物
学校元旦晚会方案
2014/02/19 职场文书
医德医魂心得体会
2014/09/11 职场文书
公司承诺函范文
2015/01/21 职场文书
酒店前台岗位职责
2015/04/16 职场文书
2015年仓管员工作总结
2015/04/21 职场文书
办公室日常管理制度
2015/08/04 职场文书
医院感染管理制度
2015/08/05 职场文书
python turtle绘图
2022/05/04 Python
Nginx静态压缩和代码压缩提高访问速度详解
2022/05/30 Servers