详解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系列之从文件读取和保存数据
May 23 Python
dataframe设置两个条件取值的实例
Apr 12 Python
Python读取视频的两种方法(imageio和cv2)
Apr 15 Python
Python多进程原理与用法分析
Aug 21 Python
对python字典过滤条件的实例详解
Jan 22 Python
python错误调试及单元文档测试过程解析
Dec 19 Python
Python面向对象之继承原理与用法案例分析
Dec 31 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧装饰器信号与槽详细使用方法与实例
Mar 06 Python
Python多进程编程multiprocessing代码实例
Mar 12 Python
python 6.7 编写printTable()函数表格打印(完整代码)
Mar 25 Python
Python中flatten( ),matrix.A用法说明
Jul 05 Python
Django缓存Cache使用详解
Nov 30 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 ctype函数中文翻译和示例
2014/03/21 PHP
php cookie名使用点号(句号)会被转换
2014/10/23 PHP
php生成年月日下载列表的方法
2015/04/24 PHP
php实现转换html格式为文本格式的方法
2016/05/16 PHP
php中的抽象方法和抽象类
2017/02/14 PHP
js 调整select 位置的函数
2008/02/21 Javascript
豆瓣网的jquery代码实例
2008/06/15 Javascript
jQuery EasyUI API 中文文档 - Calendar日历使用
2011/10/19 Javascript
ext前台接收action传过来的json数据示例
2014/06/17 Javascript
js实现获取焦点后光标在字符串后
2014/09/17 Javascript
JS中三目运算符和if else的区别分析与示例
2014/11/21 Javascript
JavaScript实现简单精致的图片左右无缝滚动效果
2017/03/16 Javascript
Bootstrap Multiselect 常用组件实现代码
2017/07/09 Javascript
详解react服务端渲染(同构)的方法
2017/09/21 Javascript
vue中使用better-scroll实现滑动效果及注意事项
2018/11/15 Javascript
jQuery实现适用于移动端的跑马灯抽奖特效示例
2019/01/18 jQuery
python实现复制整个目录的方法
2015/05/12 Python
使用python生成目录树
2018/03/29 Python
OpenCV2从摄像头获取帧并写入视频文件的方法
2018/08/03 Python
详解windows python3.7安装numpy问题的解决方法
2018/08/13 Python
pandas 对日期类型数据的处理方法详解
2019/08/08 Python
Django使用Celery加redis执行异步任务的实例内容
2020/02/20 Python
Python IDE环境之 新版Pycharm安装详细教程
2020/03/05 Python
巴西最大的体育用品商城:Netshoes巴西
2016/11/29 全球购物
DJI大疆无人机官方商城:全球领先的无人飞行器研发和生产商
2016/12/21 全球购物
英国计算机商店:Technextday
2019/12/28 全球购物
写好求职信第一句话的技巧
2013/10/26 职场文书
申报职称专业技术个人的自我评价
2013/12/12 职场文书
开服装店计划书
2014/08/15 职场文书
2014年护士个人工作总结
2014/11/11 职场文书
无罪辩护词范文
2015/05/21 职场文书
2016高考冲刺决心书
2015/09/23 职场文书
2019年房屋委托租赁合同范本(通用版)!
2019/07/17 职场文书
K8s部署发布Golang应用程序的实现方法
2021/07/16 Golang
浅谈Python3中datetime不同时区转换介绍与踩坑
2021/08/02 Python
海弦WR-800F
2022/04/05 无线电