方法一:使用装饰器实现单例模式。
from functools import wraps
def singleton(cls):
"""单例类装饰器"""
instances = {}
@wraps(cls)
def wrapper(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return wrapper
@singleton
class President:
pass
扩展:装饰器是Python中非常有特色的语法,用一个函数装饰另一个函数或类,只需添加额外的功能。通常通过装饰来实现的功能都属于横切关注功能,跟正常的功能一样业务逻辑没有必然联系,可以动态添加或删除的功能。装饰器可以为提供代码缓存、代理、上下文环境等服务,它是对设计模式中代理模式的践行。在写装饰器的时候,带装饰功能的函数(上面代码中的
wrapper
函数)通常都会用functools
模块中的wraps
加上装饰,这个装饰器最重要的作用是给被装饰的类或者函数动态添加一个__wrapped__
属性,这个属性附加在被装饰的类之前或函数保留下来的是,这样在我们需要装饰功能的时候,可以通过它来取消装饰器,例如可以使用来President = President.__wrapped__
取消对President
类做的单例处理。需要提醒大家:上面的单例不是线程安全的的,如果要做到线程安全,需要对创建对象的代码进行加锁的??处理。在Python中可以使用模块threading
的RLock
对象来提供锁,可以使用锁对象的acquire
和release
方法来实现加锁和解锁的操作。当然,最重要的做法是使用锁对象的with
上下文语法来进行隐式的加锁和解锁操作。
方法二:使用元类实现单例模式。
class SingletonMeta(type):
"""自定义单例元类"""
def __init__(cls, *args, **kwargs):
cls.__instance = None
super().__init__(*args, **kwargs)
def __call__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super().__call__(*args, **kwargs)
return cls.__instance
class President(metaclass=SingletonMeta):
pass
扩展:Python是面向对象的编程语言,在面向对象的世界中,一切皆为对象。对象是通过类来创建的,而类本身也是对象,这样的对象是通过元类来创建的。定义类的时候,如果没有给一个类指定父类,那么默认的父类就是
object
,如果没有给一个类指定元类,那么默认的元类就是type
。通过自定义的元类,我们改变一个类默认的行为,就类似于上面的代码中,我们通过元类的__call__
魔术方法,改变了President
类的构造器那样。
补充:关于单例模式,在面试中还有可能被问到它的应用场景。通常一个对象的状态是被其他对象共享的,就可以将其设计为单例,例如项目中使用的数据库连接池对象和配置对象通常都是单实例,这样才能保证所有位置获取到的数据库连接和配置信息是完全一致的;并且由于对象只有唯一的实例,因此从根本上避免了创建重复对象造成的时间和空间上面的头,也避免了对资源的占用占用。再举个例子,项目中的日志操作通常会使用单例模式,这是因为共享的日志文件一直处于打开状态,只能有一个实例去操作它,否则在写入日志的时候会产生混乱。