本文将首先回顾一下面向工具模块的紧张内容,然后重点先容基于前面先容的知识,实现单例模式。
面向工具内容梳理
关于面相工具的内容,实在我们紧张先容了三大特性、四种数据封装、三种方法、三种类。个中:
三大特性:1、封装:将数据与算法封装为一个整体,也可以理解为状态与操作的封装。
2、继续:更高等别的代码复用,子类直接拿来即用,强调一组类的共性的部分。
3、多态:子类对父类的方法的不同实现,强调子类可以丰富多彩的个性的部分。
四种数据封装:
1、实例属性:每个实例工具相互独立,可以进行私有化,虽然是“名称稠浊”的实现机制。
2、类属性:归属于类工具,所有实例工具共享该实例,同样可以私有化。
3、property:实现数据保护、添加新的校验、处理逻辑,同时兼容直接基于属性名访问的历史接口。
4、属性描述符:更加底层的一种数据封装机制,适用于很多个属性都要进行类似的封装逻辑的复用。
三种方法:
1、实例方法:通过实例工具进行调用,默认将实例工具作为方法的第一个参数进行自动通报,常日以self作为第一个形参的名字。
2、类方法:@classmethod,通过类名进行调用,与实例工具无关,不存在实例时,也是可以调用的,默认将类工具作为方法的第一个参数进行自动通报,常日以cls作为第一个形参的名字。
3、静态方法:从抽象、设计的角度,与类本身无太大关联,更多的是作为工具方法的一种抽离,以是,不会自动通报实例工具或者类工具,可以理解为类中定义的普通函数。
三种类:
1、普通类:常日定义的类,都是普通的类,可以随意进行实例化工具的创建,完成根本的数据和算法逻辑的封装。
2、抽象基类(abc):不能进行实例化操作,常日作为基类利用,定义干系协议,详细实现延迟到各个子类中。
3、元类:继续自type及其子类的类,用于创建类工具的类,通过元类,可以规范类定义以及完成类工具的动态增强。
实在,大部分内容都是在以不同的办法帮我们完成代码的复用,只是复用的颗粒度有所不同。毕竟,编程本身在于将重复事情的自动化转换,是帮助人们更好地偷
虽然花了很多的篇幅去讲解面向工具的内容,但是,真正梳理一下,也就这些东西。
单例模式的4种实现方法
抽象基类不能进行实例化,普通类可以随意进行实例化。实在,关于实例化,现实场景中,还有一种只能实例化一个工具的需求,应运而生的设计模式便是“单例模式”。
所谓“单例模式”,大略来说,便是一种设计模式,确保某个类中只能有一个实例工具,并供应一个全局访问点。
单例模式常日在资源管理、全局配置、日志记录、缓存等场景中被频繁利用,更多的是可以有效节省打算及存储资源。
方法1:利用类属性
直接看代码:
class King: _instance = None def __new__(cls, args, kwargs): if cls._instance is None: cls._instance = super().__new__(cls, args, kwargs) return cls._instanceif __name__ == '__main__': king1 = King() king2 = King() print(king1) print(king2) print(king1 is king2)
实行结果:
从实行结果,可以看到每次实例化,确实都是返回同一个实例化工具。
这种办法,实在是基于实例化工具之前,首先是须要调用__new__()方法的机制进行单例模式的,以是,须要对__new__()和__init__()方法的差异和触发机制有一个比较清晰的理解。如果单例的实例工具有状态(属性),稍不留神,可能会导致奇怪的事情的发生。
直接看代码:
class King: _instance = None def __new__(cls, args, kwargs): if cls._instance is None: cls._instance = super().__new__(cls, args, kwargs) return cls._instance def __init__(self): print(f"虽然是同一个实例,不会被重复创建,但是初始化操作会重复") self.name = '老虎'if __name__ == '__main__': king1 = King() print(king1) king1.name = '猴子' print(king1.name) king2 = King() print(king2) print(king1.name)
实行结果:
代码的本意是,既然是同一个实例,我如果修正了实例中的属性,返回的同一个实例,该当是修正后的属性。但是,__init__()方法会被重复调用的。
可以调度为以下的做法,从而避免重复初始化导致的状态丢失:
class King: _instance = None def __new__(cls, args, kwargs): if cls._instance is None: cls._instance = super().__new__(cls, args, kwargs) return cls._instance def __init__(self): if not hasattr(self, 'initialized'): self.initialized = True print(f"虽然是同一个实例,不会被重复创建,但是初始化操作会重复") self.name = '老虎'if __name__ == '__main__': king1 = King() print(king1) king1.name = '猴子' print(king1.name) king2 = King() print(king2) print(king1.name)
实行结果:
通过在实例中坚持一个实例化的状态属性,从而确保实例工具只被创建一次的同时,也只实例化一次,从而避免实例工具状态的丢失。
方法2:利用元类
利用类属性的方法实现元类,比较清晰,但是,每次创建一个单例模式的类,都要重复写单例的处理逻辑,为了更好的复用,可以利用元类来参与类工具的创建过程,从而复用单例的逻辑。
直接看代码:
class SingletonMeta(type): _instances = {} def __call__(cls, args, kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(args, kwargs) return cls._instances[cls]class King(metaclass=SingletonMeta): passif __name__ == '__main__': king1 = King() print(king1) king1.name = '猴子' print(king1.name) king2 = King() print(king2) print(king1.name)
实行结果:
通过以上两种实现方法,可以顺便回顾一下实例化工具的创建机制及元类的利用。
接下来的两种方法,涉及到我们暂未提及的Python中的内容,对新手看,可能有点超纲,以是,只是演示代码,不做过多的展开了。
方法3:利用装饰器
直接看代码:
def singleton(cls): instances = {} def get_instance(args, kwargs): if cls not in instances: instances[cls] = cls(args, kwargs) return instances[cls] return get_instance@singletonclass King: pass@singletonclass Queen: passif __name__ == '__main__': king1 = King() print(king1) king2 = King() print(king2) queen1 = Queen() print(queen1) queen2 = Queen() print(queen2)
实行结果:
方法4:利用模块的加载特性
首先在模块中定义King类,并实例化一个工具,假设模块名是singletong_module.py:
class King: passking_instance = King()print(f"模块中的king实例: {king_instance}")
然后,在须要利用该实例工具的地方进行导入:
from singleton_module import king_instanceprint(f"导入的king实例: {king_instance}")
实行结果:
须要解释的是,后面的两种实现方法,分别涉及到装饰器和模块,之以是在这里也提及一下,实在,是作为后面内容的一个提前引入。
总结
本文首先回顾了Python中面向工具的干系内容,可以大略概括为:三大特性、四种数据封装、三种方法、三种类。实在,这些内容始终环绕着一个基本点:代码复用(如何更好地偷
感谢您的拨冗阅读,如果对您学习Python有所帮助,欢迎点赞、关注。