详解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发布模块的步骤分享
Feb 21 Python
Python判断文件和文件夹是否存在的方法
May 21 Python
Django imgareaselect手动剪切头像实现方法
May 26 Python
Python绘制3D图形
May 03 Python
Python 绘制酷炫的三维图步骤详解
Jul 12 Python
python实现截取屏幕保存文件,删除N天前截图的例子
Aug 27 Python
Linux下通过python获取本机ip方法示例
Sep 06 Python
Python GUI自动化实现绕过验证码登录
Jan 10 Python
Python使用re模块验证危险字符
May 21 Python
Python3以GitHub为例来实现模拟登录和爬取的实例讲解
Jul 30 Python
Pygame框架实现飞机大战
Aug 07 Python
Pytorch中TensorBoard及torchsummary的使用详解
May 12 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中限制IP段访问、禁止IP提交表单的代码
2011/04/23 PHP
9个PHP开发常用功能函数小结
2011/07/15 PHP
ThinkPHP调试模式与日志记录概述
2014/08/22 PHP
PHP实现多维数组转字符串和多维数组转一维数组的方法
2015/08/08 PHP
CI框架常用函数封装实例
2016/11/21 PHP
PHP实现登陆表单提交CSRF及验证码
2017/01/24 PHP
thinkphp5.1 文件引入路径问题及注意事项
2018/06/13 PHP
jquery 循环显示div的示例代码
2013/10/18 Javascript
查找页面中所有类为test的结点的方法
2014/03/28 Javascript
node.js中的console.dir方法使用说明
2014/12/10 Javascript
jQuery中:lt选择器用法实例
2014/12/29 Javascript
jquery任意位置浮动固定层插件用法实例
2015/05/29 Javascript
JavaScript中判断数据类型的方法总结
2016/05/24 Javascript
将List对象列表转换成JSON格式的类实现方法
2016/07/04 Javascript
jQuery+CSS实现简单切换菜单示例
2016/07/27 Javascript
详解Angular 4.x NgTemplateOutlet
2017/05/24 Javascript
vuejs+element UI table表格中实现禁用部分复选框的方法
2019/09/20 Javascript
node.js实现简单的压缩/解压缩功能示例
2019/11/05 Javascript
angular异步验证防抖踩坑实录
2019/12/01 Javascript
javascript实现获取中文汉字拼音首字母
2020/05/19 Javascript
python中matplotlib实现最小二乘法拟合的过程详解
2017/07/11 Python
一个Python最简单的接口自动化框架
2018/01/02 Python
Django自定义manage命令实例代码
2018/02/11 Python
python爬虫正则表达式之处理换行符
2018/06/08 Python
Python实现拷贝/删除文件夹的方法详解
2018/08/29 Python
在cmd中查看python的安装路径方法
2019/07/03 Python
django xadmin action兼容自定义model权限教程
2020/03/30 Python
css3实现垂直下拉动画菜单示例
2014/04/22 HTML / CSS
韩国三星旗下的一家超市连锁店:Home Plus
2016/07/30 全球购物
单位活动策划方案
2014/08/17 职场文书
热情服务标语
2014/10/07 职场文书
会计工作总结范文2014
2014/12/23 职场文书
请学会珍惜眼前,因为人生没有下辈子!
2019/11/12 职场文书
python之np.argmax()及对axis=0或者1的理解
2021/06/02 Python
Nginx stream 配置代理(Nginx TCP/UDP 负载均衡)
2021/11/17 Servers
Netflix《海贼王》真人版剧集多张片场照曝光
2022/04/04 日漫