Python中的单例模式与反弹机制

Python08

Python中的单例模式与反弹机制,第1张

一。单例模式

一般情况下,类可以生成任意个实例,而单例模式只生成一个实例

我们先用单例模式设计一个Rectangle类

然后用__new__方法设计单例模式,代码如下

然后我们来验证下,单例模式下是否只能生成一个实例

单例模式在程序设计中比较典型的应用场景:多个用户同时调用某个模块时,会生成一些日志,我们希望这些日志存在同一个文件内,而不是多个文件。

在生成日志模块我们就可以采用单例模式进行设计。

二。反射

概念:简单来说就是可以利用字符串来映射模块中的相应方法然后可以操作模块中相应的方法

我们以一个饭店点菜的实际场景来理解Python的反射机制

hasatter(对象,属性或方法名)

判断对象中是否有某个属性或某个方法,返回值是布尔型

getattr(对象,属性或方法名,缺省值) 判断对象中是否有某个属性或某个方法,如果有返回方法本身,没有则返回缺省值

setattr(对象,属性,新值)

将实例的属性改为新的值,如果属性不存在则新建

我们给实例guke1加个价格属性

有些时候你的项目中难免需要一些全局唯一的对象,这些对象大多是一些工具性的东西,在Python中实现单例模式并不是什么难事。以下总结几种方法:

使用类装饰器

使用装饰器实现单例类的时候,类本身并不知道自己是单例的,所以写代码的人可以不care这个,只要正常写自己的类的实现就可以,类的单例有装饰器保证。

def singleton(cls):

instances = {}

def _wrapper(*args, **kwargs):

if cls not in instances:

instances[cls] = cls(*args, **kwargs)

return instances[cls]

return _wrapper

你会发现singleton装饰器内部使用了一个dict。当然你也可以用其他的方式,不过以下的实现是错误的:

def singleton(cls):

_instance = None #外部作用域的引用对于嵌套的内部作用域是只读的

def _wrapper(*args, **kwargs):

if _instance is None: #解释器会抛出"UnboundLocalError: ...referenced before assignment"

_instance = cls(*args, **kwargs) #赋值行为使解释器将"_instance"看作局部变量

return _instance

return _wrapper

使用元类(__metaclass__)和可调用对象(__call__)

Python的对象系统中一些皆对象,类也不例外,可以称之为”类型对象”,比较绕,但仔细思考也不难:类本身也是一种对象,只不过这种对象很特殊,它表示某一种类型。是对象,那必然是实例化来的,那么谁实例化后是这种类型对象呢?也就是元类。

Python中,class关键字表示定义一个类对象,此时解释器会按一定规则寻找__metaclass__,如果找到了,就调用对应的元类实现来实例化该类对象没找到,就会调用type元类来实例化该类对象。

__call__是Python的魔术方法,Python的面向对象是”Duck type”的,意味着对象的行为可以通过实现协议来实现,可以看作是一种特殊的接口形式。某个类实现了__call__方法意味着该类的对象是可调用的,可以想像函数调用的样子。再考虑一下foo=Foo()这种实例化的形式,是不是很像啊。结合元类的概念,可以看出,Foo类是单例的,则在调用Foo()的时候每次都返回了同样的对象。而Foo作为一个类对象是单例的,意味着它的类(即生成它的元类)是实现了__call__方法的。所以可以如下实现:

class Singleton(type):

def __init__(cls, name, bases, attrs):

super(Singleton, cls).__init__(name, bases, attrs)

cls._instance = None

def __call__(cls, *args, **kwargs):

if cls._instance is None

# 以下不要使用'cls._instance = cls(*args, **kwargs)', 防止死循环,

# cls的调用行为已经被当前'__call__'协议拦截了

# 使用super(Singleton, cls).__call__来生成cls的实例

cls._instance = super(Singleton, cls).__call__(*args, **kwargs)

return cls._instance

class Foo(object): #单例类

__metaclass__ = Singleton

>>>a = Foo()

>>>b = Foo()

>>>a is b

>>>True

>>>a.x = 1

>>>b.x

>>>1

使用__new__

__init__不是Python对象的构造方法,__init__只负责初始化实例对象,在调用__init__方法之前,会首先调用__new__方法生成对象,可以认为__new__方法充当了构造方法的角色。所以可以在__new__中加以控制,使得某个类只生成唯一对象。具体实现时可以实现一个父类,重载__new__方法,单例类只需要继承这个父类就好。

class Singleton(object):

def __new__(cls, *args, **kwargs):

if not hasattr(cls, '_instance'):

cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)

return cls._instance

class Foo(Singleton): #单例类

a = 1

在聊这之前我们首先要明确的是,单例模式在实际中的意义以及在python中具有实现的价值?

当前,相信有很多人支持单例模式,也有不少人反对,尤其是在python中,目前依旧具有很大的争议性。我们要在评论之前首先要了解单例模式

什么是单例模式?

顾名思义:就是单个模式

单例模式是一种常见的软件设置模式,在它的核心结构中只包含一个被称为单例类的特殊类,通过单例模式可以保证系统中的一个类只有一个实例而且该实例易于外界访问,从而方便对实例个数的控制并节约系统资源。如果希望在系统中某个对象只能存在一个,单例模式是最好的解决方案。

单例模式的要点有三类

某个类只能有一个实例

它必须创建这个实例

它必须自行向整个系统提供这个实例

但是从具体角度实现来说的话,又可以分为三点

单例模式的类只能提供私有的构造函数

类定义中含有一个该类的静态私有对象

该类提供了一个静态的共有的函数用于创建或获取它本身的静态私有对象

一、实例控制

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

二、灵活性

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

缺点:

一、开销

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

二、可能的开发混淆

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

三、对象生存期

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

常用几种方式

通过面向的特性,简单的构造出单例模式

123456789101112131415   # ########### 单例类定义 ###########class Foo(object):      __instance = None      @staticmethod    def singleton():        if Foo.__instance:            return Foo.__instance        else:            Foo.__instance = Foo()            return Foo.__instance  # ########### 获取实例 ###########obj = Foo.singleton()   

当用于WEB界面时,单例模式的简单运用

 web 单例模式

不过我们需要注意的是:

特殊方法__new__是一个元构造程序,每当一个对象必须被factory类实例化时,就将调用它。__new__方法必须返回一个类的实例,因此它可以在对象创建之前或之后修改类。

因为__init__在子类中不会被隐式调用,所以__new__可以用来确定已经在整个类层次完成了初始化构造。__new__是对于对象状态隐式初始化需求的回应,使得可以在比__init__更低的一个层次上定义一个初始化,这个初始化总是会被调用。

与__init__()相比__new__()方法更像一个真正的构造器。随着类和类型的统一,用户可以对内建类型进行派生,因此需要一种途径来实例化不可变对象,比如派生字符串,在这种情况下解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)

·__new__必须返回一个合法的实例,这样解释器在调用__init__()时,就可以吧这个实例作为self传给他。调用父类的__new__()来创建对象,正向其他语言使用new关键字一样

总结

单利模式存在的目的是保证当前内存中仅存在单个实例,避免内存浪费!!!