单例模式

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

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

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

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

  • 使用模块
  • 使用 __new__
  • 使用装饰器(decorator)
  • 使用元类(metaclass)

使用模块

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

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

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

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

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

from .tests1 import my_class_obj
my_class_obj.foo()

  

使用 __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)) #42818864 42818864

使用装饰器

我们知道,装饰器(decorator)可以动态地修改一个类或函数的功能。这里,我们也可以使用装饰器来装饰某个类,使其只能生成一个实例,代码如下:

 from functools import wraps

 def singleton(cls):
instances = {} @wraps(cls)
def getinstance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls] return getinstance @singleton
class MyClass(object):
a = 1

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

使用 metaclass

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

  • 拦截类的创建
  • 修改类的定义
  • 返回修改后的类

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

class Singleton(type):
_instances = {} def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls] # Python2
# class MyClass(object):
# __metaclass__ = Singleton # Python3
class MyClass(metaclass=Singleton):
pass

优点: 

一、实例控制
单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。
 
二、灵活性
因为类控制了实例化过程,所以类可以灵活更改实例化过程。
 

缺点:

 
一、开销
虽然数量很少,但如果每次对象请求引用时都要检查是否存在类的实例,将仍然需要一些开销。可以通过使用静态初始化解决此问题。
 
二、可能的开发混淆
使用单例对象(尤其在类库中定义的对象)时,开发人员必须记住自己不能使用 new关键字实例化对象。因为可能无法访问库源代码,因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
 
三、对象生存期
不能解决删除单个对象的问题。在提供内存管理的语言中(例如基于.NET Framework的语言),只有单例类能够导致实例被取消分配,因为它包含对该实例的私有引用。在某些语言中(如 C++),其他类可以删除对象实例,但这样会导致单例类中出现悬浮引用。
 

补充:元类(metaclass)

类也是对象

只要你使用关键字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>

动态地创建类

因为类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样。首先,你可以在函数中创建类,使用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 FooChild(Foo):
pass

就可以写成:

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实际上是一个元类。type就是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的东东都可以。

内容:自定义元类为什么要用metaclass类而不是函数?、究竟为什么要使用元类?

参考博文:http://blog.jobbole.com/21351/

python中的单例模式、元类的更多相关文章

  1. python 面向对象进阶之元类metaclass

    一:知识储备 exec exec:三个参数 参数一:字符串形式的命令 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为local ...

  2. Python学习笔记之在Python中实现单例模式

    有些时候你的项目中难免需要一些全局唯一的对象,这些对象大多是一些工具性的东西,在Python中实现单例模式并不是什么难事.以下总结几种方法: 使用类装饰器 使用装饰器实现单例类的时候,类本身并不知道自 ...

  3. 神级程序员通过两句话带你完全掌握Python最难知识点——元类!

    千万不要被所谓"元类是99%的python程序员不会用到的特性"这类的说辞吓住.因为 每个中国人,都是天生的元类使用者 学懂元类,你只需要知道两句话: 道生一,一生二,二生三,三生 ...

  4. 非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感

    关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针) 我读完后的理解总结: 1. 我们知道,python中的变量的赋值操作,变量其实就是一个名称name,赋值就是将name引用到一个 ...

  5. python中如何统计一个类的实例化对象

    类中的静态变量 需要通过类名.静态变量名 来修改 :通过对象不能修改 python中如何统计一个类的实例化对象?? class Person: #静态变量count,用于记录类被实例化的次数 coun ...

  6. python 入门基础24 元类、单例模式

    内容目录: 一.元类 二.单例模式 一.元类 1 什么是元类: 源自一句话:在python中,一切皆对象,而对象都是由类实例化得到的 class OldboyTeacher: def __init__ ...

  7. python 之 面向对象(元类、__call__、单例模式)

    7.13 元类 元类:类的类就是元类,我们用class定义的类来产生我们自己的对象的,内置元类type是用来专门产生class定义的类 code=""" global x ...

  8. Python中的单例模式

    在 Python 中,我们可以用多种方法来实现单例模式: 使用模块 使用 __new__ 使用装饰器(decorator) 使用元类(metaclass) # mysingleton.py class ...

  9. Python中的单例模式的几种实现方式的优缺点及优化

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

随机推荐

  1. Python深入类和对象

    一. 鸭子类型和多态 1.什么是鸭子类型: 在程序设计中,鸭子类型(英语:Duck typing)是动态类型和某些静态语言的一种对象推断风格."鸭子类型"像多态一样工作,但是没有继 ...

  2. placeholder解决兼容各种IE浏览器的方法

    <input id="search" type="text" class="box" class="inputText&qu ...

  3. python数据结构算法学习自修第一天【数据结构与算法引入】

    1.算法引入: #!/usr/bin/env python #! _*_ coding:UTF-8 _*_ from Queue import Queue import time que = Queu ...

  4. eclipse 启动问题Eclipse启动时报错:A Java RunTime Environment (JRE) or Java Development Kit (JDK) must be available in order to run Eclipse. No java virtual machine was found after searching the following locat

    从其他人直接复制的环境导致的问题. 正常双击出现当前异常,以管理员权限启动可以正常启动. ---------------------------Eclipse--------------------- ...

  5. 四、K8S

    一.查看日志 journalctl -xeu kubelet

  6. 【python练习题】程序2

    2.题目:企业发放的奖金根据利润提成.利润(I)低于或等于10万元时,奖金可提10%:利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成7.5%:20万到40 ...

  7. Installing Office Online Server for SharePoint 2016

    Office Online Server is the next version of the Office Web Apps, which allows your users to view and ...

  8. AtCoder Beginner Contest 122 解题报告

    手速选手成功混进rated only里面的前30名,但是总排名就到110+了... A - Double Helix #include <bits/stdc++.h> #define ll ...

  9. codeforces347B

    Fixed Points CodeForces - 347B A permutation of length n is an integer sequence such that each integ ...

  10. winFormToMysql&&几个控件的数据绑定

    运行图: 代码: private void button1_Click(object sender, EventArgs e) { string str = "database=csharp ...