详解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函数之作用域(python3.5)
Oct 27 Python
Python字符串拼接六种方法介绍
Dec 18 Python
基于python的图片修复程序(实现水印去除)
Jun 04 Python
Python异步操作MySQL示例【使用aiomysql】
May 16 Python
python 实现将文件或文件夹用相对路径打包为 tar.gz 文件的方法
Jun 10 Python
python实发邮件实例详解
Nov 11 Python
Python批量安装卸载1000个apk的方法
Apr 10 Python
如何在python中执行另一个py文件
Apr 30 Python
pytorch查看模型weight与grad方式
Jun 24 Python
解决tensorflow/keras时出现数组维度不匹配问题
Jun 29 Python
python+appium+yaml移动端自动化测试框架实现详解
Nov 24 Python
Python基础之字符串格式化详解
Apr 21 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 编程请选择正确的文本编辑软件
2006/12/21 PHP
php分页示例代码
2007/03/19 PHP
php短网址和数字之间相互转换的方法
2015/03/13 PHP
PHP 类与构造函数解析
2017/02/06 PHP
php基于session锁防止阻塞请求的方法分析
2017/08/07 PHP
PhpSpreadsheet设置单元格常用操作汇总
2020/11/13 PHP
一页面多XMLHttpRequest对象
2007/01/22 Javascript
给网站上的广告“加速”显示的方法
2007/04/08 Javascript
Iframe 自动适应页面的高度示例代码
2014/02/26 Javascript
js实现左侧网页tab滑动门效果代码
2015/09/06 Javascript
原生JS取代一些JQuery方法的简单实现
2016/09/20 Javascript
axios异步提交表单数据的几种方法
2019/08/11 Javascript
JavaScript canvas基于数组生成柱状图代码实例
2020/03/06 Javascript
jQuery AJAX应用实例总结
2020/05/19 jQuery
解决VUE项目使用Element-ui 下拉组件的验证失效问题
2020/11/07 Javascript
如何在vue中使用video.js播放m3u8格式的视频
2021/02/01 Vue.js
Python安装官方whl包和tar.gz包的方法(推荐)
2017/06/04 Python
Python设计模式之中介模式简单示例
2018/01/09 Python
python的Crypto模块实现AES加密实例代码
2018/01/22 Python
python数据封装json格式数据
2018/03/04 Python
Python使用装饰器模拟用户登陆验证功能示例
2018/08/24 Python
OpenCV图像颜色反转算法详解
2019/05/13 Python
django自带调试服务器的使用详解
2019/08/29 Python
Python 可变类型和不可变类型及引用过程解析
2019/09/27 Python
python绘制随机网络图形示例
2019/11/21 Python
Skip Hop官网:好莱坞宝宝挚爱品牌
2018/06/17 全球购物
盛大二次面试题
2016/11/18 面试题
晚宴邀请函范文
2014/01/15 职场文书
小学感恩节活动策划方案
2014/10/06 职场文书
优秀党员先进材料
2014/12/18 职场文书
2014年加油站站长工作总结
2014/12/23 职场文书
文明礼仪倡议书
2015/04/28 职场文书
联欢会开场白
2015/06/01 职场文书
幼儿园庆元旦主持词
2015/07/06 职场文书
python b站视频下载的五种版本
2021/05/27 Python
mysql通过group by分组取最大时间对应数据的两种有效方法
2022/09/23 MySQL