一、单例模式

  单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。

  当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。例如,某个服务器程序的配置信息存放在一个文件中,客户通过一个AppConfig的类来读取配置文件的信息。如果在程序运行期间,有很多地方需要使用配置文件的内容,也就是说,很多地方都需要创建AppConfig对象的实例,这就导致系统中存在多个AppConfig的实例对象,而这样会严重浪费内存资源,尤其实在配置文件内容很多的情况下。事实上,类似AppConfig这样的类,我们希望在程序运行期间只存在一个实例对象。

  单例模式的要点有三个,一个是某个类只能有一个实例,二是它必须自行创建这个实例,三是它必须自行向整个系统提供这个实例。

  在Python中,我们可以使用多种方法来实现单例模式:

    1. 使用模块

    2. 使用__new__方法

    3. 使用装饰器decorator

    4. 使用类

    5. 使用元类metaclass

  1.使用模块

  其实,Python的模块就是天然的单例模式。

  因为模块在第一次导入的时候,会生成.pyc文件,当第二次导入时,就会直接加载.pyc文件,而不会再次执行模块代码。因此,我们只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象。

  如果我们真的想要一个单例类,可以考虑这样做:

class MyClass(object):
def foo(self):
print('MyClass.foo') my_class_obj = MyClass()

  将上面的代码保存在文件test1.py中,然后这样使用:

from .test1 import my_class_obj

my _class_obj.foo()

  

  2.使用__new__

  为了使类只能出现一个实例,我们可以使用__new__来控制实例的创建过程,代码如下:

class MyClass(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(MyClass, cls).__new__(cls, *args, **kwargs)
return cls._instance class HerClass(MyClass):
a = 1

  在上面的代码中,我们将类的实例和一个类变量_instance关联起来,如果cls._instance为None则创建实例,否则直接返回cls._instance。

  执行情况如下:

one = HerClass()
two = HerClass()
print(one == two) #True
print(one is two) #True
print(id(one) == id(two)) #True

  3. 使用装饰器

  我们知道,装饰器decorator可以动态地修改一个类或者函数的功能。

  这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下:

from functools import wraps

def singleton(cls):
instance = {} #创建字典,盛放单例 @wraps
def getinstance(*args,**args): if cls not in instance: #cls不在字典
instances[cls] = cls(*args, **kwargs)
#以cls为key,cls(*args, **kwargs) 为值放入盛放单例的字典 return getinstance[cls] return getinstance @singleton
class MyClass(object):
a = 1

  在上面,我们定义要给装饰器singleton,它返回了一个内部函数getinstance,该函数会判断某个类是否在字典instance中,如果不存在,则会将cls作为key, cls(*args,**kwargs)作为value存到instance中,否则,直接返回instance[cls]。

class Singleton(object):

    def __init__(self):
pass @classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance import threading def task(arg):
obj = Singleton.instance()
print(obj) for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()

  执行结果:

<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>
<__main__.Singleton object at 0x02C933D0>

  看起来也没有问题,那是因为执行速度过快。

  如果在init方法中有一些IO操作,就会发现问题了,下面我们通过time.sleep模拟

  我们在上面__init__方法中加入以下代码

def __init__(self):
import time
time.sleep(1)

  重新执行程序后,结果如下

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>

  问题出现了!按照以上方式创建的单例,无法支持多线程

  解决办法:加锁!未加锁部分并发执行,加锁部分串行执行,速度降低,但是保证了数据安全

import time
import threading
class Singleton(object):
_instance_lock = threading.Lock() def __init__(self):
time.sleep(1) @classmethod
def instance(cls, *args, **kwargs):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)

  打印结果如下:

<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>
<__main__.Singleton object at 0x02D6B110>

  这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行:

   @classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance

  这样,一个可以支持多线程的单例模式就完成了

import time
import threading
class Singleton(object):
_instance_lock = threading.Lock() def __init__(self):
time.sleep(1) @classmethod
def instance(cls, *args, **kwargs):
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
if not hasattr(Singleton, "_instance"):
Singleton._instance = Singleton(*args, **kwargs)
return Singleton._instance def task(arg):
obj = Singleton.instance()
print(obj)
for i in range(10):
t = threading.Thread(target=task,args=[i,])
t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)

  这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance()

  如果用 obj=Singleton() ,这种方式得到的不是单例

  

  4. 使用类

class Singleton(object):

    def __init__(self):
pass @classmethod
def instance(cls,*args,**kwargs):
if not hasattr(Singleton, '_instance'):
Singleton._instace = Singleton(*args,**kwargs)
return Singleton._instance

  一般情况,大家以为这样就完成了单例模式,但是这样当使用多线程时会存在问题

  

  5. 使用metaclass

  元类metaclass,可以控制类的创建过程,它主要做三件事:

    1. 拦截类的创建

    2. 修改类的定义

    3. 返回修改后的类

  使用元类实现单例模式的代码如下:

class Singleton(type):
_instacne = {} def __call__(cls,*args,**kwargs): if cls not in cls._instance:
cls._instances[cls] = super(Singleton, cls).__call__(*args,**kwargs)
         #以cls为key,cls(*args, **kwargs) 为值放入盛放单例的字典 return cls._instance[cls]
#Python2
#class MyClass(object):
__metaclass__ = Singleton #Python3
class MyClass(metaclass=Singleton):
pass

  优点:

  1. 实例控制

    单例模式会阻止其他对象实例化器自己的单例对象的副本,从而确保所有对象都访问唯一实例。

  2. 灵活性

    因为类控制了实例化过程,所以类可以灵活更改实例化过程。

  缺点:

  1. 开销

    虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。

  2. 可能的开发混淆

    使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。

  3. 对象生存期

    不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.net Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(例如C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。

二、元类(metaclass)

  1. Python中一切皆对象,类也是对象。

    只要你使用关键字class,Python解释器在执行的时候会创建一个对象。

    下面的代码段:

class MyClass(object):
pass

    在内存中创建一个对象,名字就是MyClass。这个对象(类)自身拥有创建对象(类实例)的能力,而这就是为什么它是一个类的原因。但是,它本质仍然是一个对象,于是你可以对它做如下操作:

      1. 你可以将它赋值给一个变量

      2. 你可以拷贝它

      3. 你可以为它增加属性

      4. 你可以将它作为函数参数进行传递

class MyClass(object):
pass print(MyClass) # 你可以打印这个类,因为它是对象
# <class '__main__.MyClass'> def echo(o):
print(o) echo(MyClass) # 你可以将类作为参数传给函数
# <class '__main__.MyClass'> MyClass.new_attribute = 'foo' #你可以为类增加属性
print(hasattr(MyClass,'new_attribute')) #True
print(MyClass.new_attribute) #foo MyClassMirror = MyClass #你可以将类赋值给一个变量
print(MyClassMirror())
# <__main__.MyClass object at 0x00000000028CDE10>

  2. 动态的创建类

    因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。

     首先你可以在函数中创建类,使用class关键字即可。

def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # 返回的是类,不是类的实例
else:
class Bar(object):
pass
return Bar MyClass = choose_class('foo')
print(MyClass) #函数返回的是类,不是类的实例
# <class '__main__.choose_class.<locals>.Foo'> print(MyClass()) #你可以通过这个类创建类实例,也就是对象
# <__main__.choose_class.<locals>.Foo object at 0x00000000021E5CF8>

    但是这还不够动态。

    由于类也是对象,所以它们必须通过什么东西生成才对。

    还记得内建函数type吗?这个祖先级别的函数能够让你知道一个对象的类型是什么,如下代码:

print(type(1))     #<class 'int'>
print(type('')) ##<class 'str'>
print(type(MyClass)) #<class 'type'>
print(type(MyClass())) #<class '__main__.MyClass'>

    type也能动态的创建类。

      type创建类的模版:

type(类名,父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

    比如下面的代码:

class MyShinyClass(object):
pass

    可以手动像这样创建:

MyShinyClass = type('MyShinyClass',(),{})
print(MyShinyClass)
# <class '__main__.MyShinyClass'> print(MyShinyClass()) # 创建一个该类的实例
# <__main__.MyShinyClass object at 0x0000000002737D68>

    type接受一个字典来为类定义属性,因此:

class Foo(object):
bar = True

    可以翻译为:

Foo = type('Foo',(),{'bar':True})

    并且可以将Foo当成一个普通的类一样使用:

class Foo(object):
bar=True print(Foo) #<class '__main__.Foo'>
print(Foo.bar) #True
f=Foo()
print(f) #<__main__.Foo object at 0x0000000001F7DE10>
print(f.bar) #True

    当然,你可以向这个类继承,所以,如下的代码:

class Foo(object):
bar=True FooChild = type('FooChild', (Foo,),{})
print(FooChild)
# <class '__main__.FooChild'> print(FooChild.bar) # bar属性是由Foo继承而来
# True

    最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。

class Foo(object):
bar=True def echo_bar(self):
print(self.bar) FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
print(hasattr(Foo, 'echo_bar')) #False
print(hasattr(FooChild, 'echo_bar')) #True
my_foo = FooChild()
my_foo.echo_bar() #True

    你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。

    到底什么是元类?

    元类就是用来创建类的“东西”。元类就是类的类。

    type就是创建类对象的类。你可以通过检查__class__属性来看到这一点。 

    Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象,而且它们都是从一个类创建而来。

age = 35
print(age.__class__) #<class 'int'> name = 'bob'
print(name.__class__) #<class 'str'> def foo():
pass
print(foo.__class__) #<class 'function'> class Bar(object):
pass
b = Bar()
print(b.__class__) #<class '__main__.Bar'>

    现在,对于任何一个__class__的__class__属性又是什么呢?

print(age.__class__.__class__)  #<class 'type'>
print(name.__class__.__class__) #<class 'type'>
print(foo.__class__.__class__) #<class 'type'>
print(b.__class__.__class__) #<class 'type'>

    因此,元类就是创建类这种对象的东西。

    type就是Python的内建元类,当然了,你也可以创建自己的元类。

    __metaclass__属性

    你可以在写一个类的时候为其添加__metaclass__属性。

class Foo(object):
__metaclass__ = something...

     如果你这么做了,Python就会用元类来创建类Foo。小心点,这里面有些技巧。你首先写下class Foo(object),但是类对象Foo还没有在内存中创建。Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。

class Foo(Bar):
pass 

    Python做了如下的操作:

    Foo中有__metaclass__这个属性吗?如果是,Python会在内存中通过__metaclass__创建一个名字为Foo的类对象(我说的是类对象,请紧跟我的思路)。如果Python没有找到__metaclass__,它会继续在Bar(父类)中寻找__metaclass__属性,并尝试做和前面同样的操作。如果Python在任何父类中都找不到__metaclass__,它就会在模块层次中去寻找__metaclass__,并尝试做同样的操作。如果还是找不到__metaclass__,Python就会用内置的type来创建这个类对象。

    现在的问题就是,你可以在__metaclass__中放置些什么代码呢?

    答案就是:可以创建一个类的东西。那么什么可以用来创建一个类呢?type,或者任何使用到type或者子类化type的东东都可以。

参考

Python入门之Python的单例模式和元类的更多相关文章

  1. Python 入门之 Python三大器 之 装饰器

    Python 入门之 Python三大器 之 装饰器 1.开放封闭原则: (1)代码扩展进行开放 ​ 任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改.所以我们必须允许代 ...

  2. Python 入门之 Python三大器 之 生成器

    Python 入门之 Python三大器 之 生成器 1.生成器 (1)什么是生成器? 核心:生成器的本质就是一个迭代器 迭代器是Python自带的 生成器程序员自己写的一种迭代器 def func( ...

  3. Python入门之 Python内置函数

    Python入门之 Python内置函数 函数就是以功能为导向,一个函数封装一个功能,那么Python将一些常用的功能(比如len)给我们封装成了一个一个的函数,供我们使用,他们不仅效率高(底层都是用 ...

  4. Python 入门之Python基础数据类型及其方法

    Python 入门之Python基础数据类型 1. 整型:int 用于计算,用于比较 (在赋值的时候先执行等号右边的内容) 1.1 整数的加 a = 10 b = 20 print(a + b) 结果 ...

  5. Python 入门之Python简介

    Python 入门之Python简介 1.Python简介: (1) Python的出生: ​ python的创始人为吉多·范罗苏姆(Guido van Rossum)(中文名字:龟叔).1989年的 ...

  6. Python 入门之 Python三大器 之 迭代器

    Python 入门之 Python三大器 之 迭代器 1.迭代器 (1)可迭代对象: <1> 只要具有__ iter __()方法就是一个可迭代对象 (我们可以通过dir()方法去判断一个 ...

  7. Python 入门之Python基础知识

    Python 入门之Python基础知识 1.变量 (1)变量就是把程序运行的中间结果临时存在内存中,以便后续代码使用 (2)变量的作用: 昵称,就是代指内存中某个地址中的内容 a = 123 变量名 ...

  8. python中的单例模式、元类

    单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场. ...

  9. 编写高质量的Python代码系列(四)之元类及属性

    元类(metaclass)及动态属性(dynamic attribute)都是很强大的Python特性,然后他们也可能导致及其古怪.及其突然的行为.本节讲解这些机制的常见用法,以确保各位程序员写出来的 ...

随机推荐

  1. django比较相等或者不相等的模板语法ifequal / ifnotequal

    转自:http://blog.csdn.net/goupper1991/article/details/50768346 ifequal / ifnotequal      在模板语言里比较两个值并且 ...

  2. Spring Data JPA(官方文档翻译)

    关于本书 介绍 关于这本指南 第一章 前言 第二章 新增及注意点 第三章 项目依赖 第四章 使用Spring Data Repositories 4.1 核心概念 4.2 查询方法 4.3 定义rep ...

  3. 洛谷P3368 树状数组2 树状数组+差分

    正解:树状数组+差分 解题报告: 戳我! 不得不说灵巧真滴是越来越弱了...连模板题都要放上来了QAQ 因为今天考试的T3正解要用到树状数组这才惊觉树状数组掌握得太太太太差了...之前一直靠线段树续着 ...

  4. 走进C++程序世界------IO标准库介绍

    流概述    流是C++标准的组成部分,流的主要目标是,将从磁盘读取文件或将输入写入控制台屏幕的问题封装起来,创建流后程序猿就能够使用它.流将负责处理全部的细节. IO类库 在C++输入、输出操作是通 ...

  5. 怎样知道 CPU 是否支持虚拟化技术(VT) | Linux 中国

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/F8qG7f9YD02Pe/article/details/79832475 wx_fmt=png&a ...

  6. Python高阶函数(Map、Reduce、Filter)

    Map函数 map()函数接收两个参数,一个是函数,一个是序列,map将传入的函数依次作用到序列的每个元素,并把结果作为新的list返回. 举例说明 比如我们有一个函数f(x)=x2,要把这个函数作用 ...

  7. 16 jmeter中的监听器以及测试结果分析

    常用监听器 断言结果.查看结果树.聚合报告.Summary Report.用表格查看结果.图形结果.aggregate graph等 指标分析 -Samples:本次场景中一共完成了多少请求-Aver ...

  8. Git、bower 安装

    1>下载并安装nodejs .老师分享的nodejs版本“node-v8.9.4-x64” 下载页面http://nodejs.cn/download/     一直无脑下一步操作即可安装完毕 ...

  9. [dt]世纪历史长河年代表

    年代口诀 夏商与西周, 东周分两段, 春秋和战国, 一统秦两汉, 三分魏蜀吴, 二晋前后延, 南北朝并列, 隋唐五代传, 宋元明清后, 皇朝至此完. 中国历史长河年代表 参考: 中国历史朝代顺序表.年 ...

  10. [LeetCode] 628. Maximum Product of Three Numbers_Easy

    Given an integer array, find three numbers whose product is maximum and output the maximum product. ...