详解python单例模式与metaclass

作者:quietin 时间:2021-03-20 23:43:56 

单例模式的实现方式

将类实例绑定到类变量上


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
0
投稿

猜你喜欢

  • python中list列表的高级函数

    2021-02-24 08:21:14
  • Python爬虫获取豆瓣电影并写入excel

    2022-04-24 06:41:12
  • Study jQuery in a Simplified Way

    2010-01-30 12:55:00
  • PHP生成HTML静态页面实例代码

    2023-10-28 12:05:01
  • PHP易混淆知识整理笔记

    2023-06-10 04:34:22
  • 利用PyQt5模拟实现网页鼠标移动特效

    2022-11-17 10:46:01
  • python引入requests报错could not be resolved解决方案

    2022-08-28 08:24:45
  • 用python标准库difflib比较两份文件的异同详解

    2023-01-29 07:34:29
  • Python+requests+unittest执行接口自动化测试详情

    2023-07-30 15:08:37
  • 详解Python requests模块

    2021-12-31 21:55:12
  • 浅谈numpy.where() 的用法和np.argsort()的用法说明

    2022-03-12 01:52:23
  • pygame游戏之旅 计算游戏中躲过的障碍数量

    2022-02-13 19:09:16
  • 解决linux下使用python打开terminal时报错的问题

    2022-10-06 10:46:25
  • PHP循环与分支知识点梳理

    2023-05-29 13:09:21
  • python 解析html之BeautifulSoup

    2021-02-09 23:09:12
  • laravel实现简单用户权限的示例代码

    2023-11-14 11:58:29
  • python模块itsdangerous简单介绍

    2022-03-19 14:28:28
  • PHP crypt()函数的用法讲解

    2023-06-13 03:08:05
  • Python字典常见操作实例小结【定义、添加、删除、遍历】

    2021-02-18 20:42:41
  • 随滚动条移动的DIV层js代码

    2007-10-10 12:51:00
  • asp之家 网络编程 m.aspxhome.com