来自 编程应用 2019-09-04 19:47 的文章
当前位置: 六合联盟网 > 编程应用 > 正文

Python单例形式和工厂情势学习笔记,Python设计形

注:使用的是Python 2.7。

一. 单例模式( Singleton )

一个简单实现

所谓单例模式,也就是说不管什么时候都要确保只有一个对象实例存在。很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线程池。这些场景下,就非常适合使用单例模式。

复制代码 代码如下:

总结起来,就是说不管初始化一个对象多少次,真正干活的对象只会生成一次并且在首次生成。

class Foo(object):
    __instance = None
    def __init__(self):
        pass
    @classmethod
    def getinstance(cls):
        if(cls.__instance == None):
            cls.__instance = Foo()
        return cls.__instance

用Python 实现单例模式的方法有很多,先来看第一种方式。

if __name__ == '__main__':
    foo1 = Foo.getinstance()
    foo2 = Foo.getinstance()
    print id(foo1)
    print id(foo2)
    print id(Foo())

# !/usr/bin/python3
# -*- coding:utf-8 -*-

输出的前两个结果是相同的(id(foo1)与id(foo2)的值相同),第三个结果和前两个不同。这里类方法getinstance()用于获取单例,但是类本身也可以实例化,这样的方式其实并不符合单例模式的要求。但是这样做也有好处,代码简单,大家约定好这样子调用就行了。但是最好在类的命名上也体现了出来这是一个单例类,例如Foo_singleton。

class singleton(object):
    """
    单列模式
    """

换一个思路

    class _A(object):
        """
        真正干活的类,对外隐藏。
        """

先说一下init和new的区别:

        def __init__(self):
            pass

复制代码 代码如下:

        def display(self):
            """ 返回当前实例的ID,是全局唯一的"""
            return id(self)

class Foo(object):
    __instance = None
    def __init__(self):
        print 'init'
if __name__ == '__main__':
    foo = Foo()

    # 类变量,用于存储_A 的实例
    _instance = None

运行结果是:

    def __init__(self):
        """ 先判断类变量中是否已经保存了_A 的实例,如果没有则创建一个后返回"""
        if singleton._instance is None:
            singleton._instance = singleton._A()

复制代码 代码如下:

    def __getattr__(self, attr):
        return getattr(self._instance, attr)

init

if __name__ == "__main__":
    # 创建2个实例
    s1 = singleton()
    s2 = singleton()
    print(id(s1), s1.display())
    print(id(s2), s2.display())

而下面的示例:

使用装饰器

复制代码 代码如下:

装饰器维护一个字典对象instances,缓存了所有单例类,只要单例不存在则创建,已经存在直接返回该实例对象。

class Foo(object):
    __instance = None
    def __init__(self):
        print 'init'
    def __new__(cls, *args, **kwargs):
        print 'new'

def singleton(cls):
    instances = {}

if __name__ == '__main__':
    foo = Foo()

    def wrapper(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

运行结果是:

    return wrapper

复制代码 代码如下:

@singleton
class Foo(object):
    pass

new

foo1 = Foo()
foo2 = Foo()

new是一个类方法,会创建对象时调用。而init方法是在创建完对象后调用,对当前对象的实例做一些一些初始化,无返回值。如果重写了new而在new里面没有调用init或者没有返回实例,那么init将不起作用。以下内容引用自

print foo1 is foo2   # True

复制代码 代码如下:

使用基类

If __new__() returns an instance of cls, then the new instance's __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__().

__new__ 是真正创建实例对象的方法,所以重写基类的 __new__ 方法,以此来保证创建对象的时候只生成一个实例

If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked.

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):
    pass

复制代码 代码如下:

foo1 = Foo()
foo2 = Foo()

class Foo(object):
    __instance = None
    def __init__(self):
        print 'init'

print foo1 is foo2  # True

    def __new__(cls, *args, **kwargs):
        print 'new'
        if cls.__instance == None:
            cls.__instance = cls.__new__(cls, *args, **kwargs)
        return cls.__instance

使用元类

if __name__ == '__main__':
    foo = Foo()

元类(参考: 深刻理解Python中的元类 )是用于创建类对象的类,类对象创建实例对象时需要一定会调用 __call__ 方法,因此在调用 __call__ 时候保证始终只创建一个实例即可, type 是python中的一个元类。

    错误如下:

class Singleton(type):
    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            cls._instance = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instance

复制代码 代码如下:

class Foo(object):
    __metaclass__ = Singleton

RuntimeError: maximum recursion depth exceeded in cmp

foo1 = Foo()
foo2 = Foo()

而这样也有一样的错误:

print foo1 is foo2  # True

复制代码 代码如下:

# !/usr/bin/python3 # -*- coding:utf-8 -*- class singleton(object): """ 单列模式 """ class _A(object): """ 真正干活的类,对外隐藏。 """ def __init__ (self): pass def display(self): """ 返回当前实例的ID,是全局唯一的 """ return id(self) # 类变量,用于存储_A 的实例 _instance = None def __init__ (self): """ 先判断类变量中是否已经保存了_A 的实例,如果没有则创建一个后返回 """ if singleton._instance is None: singleton._instance = singleton._A() def __getattr__ (self, attr): return getattr(self._instance, attr) if __name__ == " __main__ " : # 创建2个实例 s1 = singleton() s2 = singleton() print (id(s1), s1.display()) print (id(s2), s2.display())

class Foo(object):
    __instance = None
    def __init__(self):
        if self.__class__.__instance == None:
            self.__class__.__instance = Foo()
        print 'init'

Output:

if __name__ == '__main__':
    foo = Foo()

1917919737784 1917919737896
1917919738008 1917919737896

该怎么做呢?

View Code
以上的单列模式实现中,借助了使用类变量 Singleton._instance 来存储创建的实例,并且保证只会创建一次实例。由于 Python 是一门动态语言,可以在运行时改变类定义。上面的代码中,在首次初始化 Singleton 时,将首次生成类 _A 的实例,并将其存储到 Singleton._instance 中,以后每次初始化 Singleton 时都从 Singleton._instance 获取真正干活的实例,这样就实现了单例模式。

下面参考了

从输出中可以看到,尽管创建了两个不同的实例(实例 ID 不同),但是访问其属性的时候得到的 ID 是一致的。以上代码虽然很好的实现了单例模式,但是在真正的项目开发中这种方式却不够灵活,因为要将真正干活的类内置在单例类中。 我们知道在 Python 中装饰器很好用,那能否将单例类实现成一个装饰器呢?答案是肯定的,下面实现代码。

复制代码 代码如下:

# !/usr/bin/python3
# -*- coding:utf-8 -*-
import sqlite3
from flask import current_app
from flask import _app_ctx_stack as stack

class Foo(object):
    __instance = None
    def __new__(cls, *args, **kwargs):
        print 'hhhhhhhhh'
        if not cls.__instance:
            cls.__instance = super(Foo, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

class SQLite3(object):
    def __init__(self, app=None):
        self.app = app
        if app is not None:
            self.init_app(app)

    def hi(self):
        print 'hi, world'
        print 'hi, letian'

def init_app(self, app):
    """典型的 Flask 扩展的初始化方式"""
    app.config.setdefault('SQLITE3_DATABASE', ':memory:')
    app.teardown_appcontext(self.teardown)

if __name__ == '__main__':
    foo1 = Foo()
    foo2 = Foo()
    print id(foo1)
    print id(foo2)
    print isinstance(foo1, object)
    print isinstance(foo1, Foo)
    foo1.hi()

def connect(self):
    """连接到 sqlite 数据库"""
    return sqlite3.connect(current_app.config['SQLITE3_DATABASE'])

运行结果:

def teardown(self, exception):
    """关闭 sqlite 链接"""
    ctx = stack.top
    if hasattr(ctx, 'sqlite3_db'):
        ctx.sqlite3_db.close()

复制代码 代码如下:

@property
def connection(self):
    """
    单例模式在这里:使用 flask._app_ctx_stack 存放 sqlite 链接,
    每次获取数据库链接时都通过 connection 获取
    """
    ctx = stack.top
    if ctx is not None:
        if not hasattr(ctx, 'sqlite3_db'):
            ctx.sqlite3_db = self.connect()
        return ctx.sqlite3_db

hhhhhhhhh
hhhhhhhhh
39578896
39578896
True
True
hi, world
hi, letian

在以上的代码中,在每次使用数据库的时候通过 SQLite3.connection 获取数据库连接就可以了。 SQLite3.connection 保证了数据库连接只会发生一次,其原理和之前实现单例模式的方式相同,只不过这里存储实例的地方变成 flask._app_ctx_stack 了。

那么,到底发生了什么,我们先回顾一下super:

通过以上几段代码,可以看到单例模式的实现只需要找一个变量存放创建的实例,然后每次获取实例时,先检查变量中是否已保存实例,如果没有则创建一个实例并将其存放到变量中,以后都从这个变量中获取实例就可以了。单例模式中,只会创建一次实例。

复制代码 代码如下:

>>> print super.__doc__
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
Typical use to call a cooperative superclass method:
class C(B):
    def meth(self, arg):
        super(C, self).meth(arg)

可以肯定上面的单例模式代码中的这一行代码:

复制代码 代码如下:

cls.__instance = super(Foo, cls).__new__(cls, *args, **kwargs)

super(Foo, cls)是object,super(Foo, cls).new方法使用的是object的new方法。我们看一下object.new方法的作用:

复制代码 代码如下:

>>> print object.__new__.__doc__
T.__new__(S, ...) -> a new object with type S, a subtype of T

如果是一个继承链

复制代码 代码如下:

class Fo(object):
    def __new__(cls, *args, **kwargs):
        print 'hi, i am Fo'
        return  super(Fo, cls).__new__(cls, *args, **kwargs)

class Foo(Fo):
    __instance = None
    def __new__(cls, *args, **kwargs):
        if not cls.__instance:
            print Foo is cls
            print issubclass(cls, Fo)
            print issubclass(cls, object)
            cls.__instance = super(Foo, cls).__new__(cls, *args, **kwargs)
        return cls.__instance

    def hi(self):
        print 'hi, world'

if __name__ == '__main__':
    foo1 = Foo()
    foo1.hi()
    print isinstance(foo1, Foo)
    print isinstance(foo1, Fo)
    print isinstance(foo1, object)

运行结果如下:

复制代码 代码如下:

True
True
True
hi, i am Fo
hi, world
True
True
True

如果如下定义Fo,也正常运行:

复制代码 代码如下:

class Fo(object):
    pass

但是,若这样定义:

复制代码 代码如下:

class Fo(object):
    def __new__(cls, *args, **kwargs):
        print 'hi, i am Fo'

运行时报错如下:

复制代码 代码如下:

AttributeError: 'NoneType' object has no attribute 'hi'

2.7。 一个简单实现 复制代码 代码如下: class Foo(object): __instance = None def __init__(self): pass @classmethod def getinstance(cls): if(cl...

本文由六合联盟网发布于编程应用,转载请注明出处:Python单例形式和工厂情势学习笔记,Python设计形

关键词: