python常用设计模式解析
一. 什么是python的设计模式?
软件工程中,设计模式是指软件设计问题的推荐方案。设计模式一般是描述如何组织代码和使用最佳实践来解决常见的设计问题。需谨记一点:设计模式是高层次的方案,并不关注具体的实现细节,比如算法和数据结构。对于正在尝试解决的问题,何种算法和数据结构最优,则是由软件工程自己把握。面试经常会问到设计模式,所以我给大家准备一些常用的设计模式,也可以更好的与面试官交流。
二. python实现设计模式
设计模式共分为三大类,细分为23种设计模式。
- 创建型模式
- 结构型模式
- 行为型模式
2.1创建型模式
单例模式
单例模式(Singleton Pattern)是一个常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在,当希望在某个系统中只出现一个实例时,单例对象就能派上用场。比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。
class Singleton(): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super().__new__(cls) return cls._instance s1 = Singleton() s2 = Singleton() print(id(s1),id(s2))
工厂模式
工厂模式是一个在软件开发中用来创建对象的设计模式。
在工厂模式设计中,客户端可以请求一个对象,而无需知道这个对象来自哪里(使用类来生成对象)也就是说,客户只需要调用指定的函数或者方法,传入具有辨别性质的参数(如下面代码的"M","F")得到的返回值就是最终的对象。工厂背后的思想是简化对象的创建,基于一个中心化的函数(定义一个抽象的实例化对象的接口)来实现创建对象,(类似于我在面向对象的类的约束中讲过的归一化设计)通过将创建对象的代码和使用对象的代码解耦,工厂能够降低应用维护的复杂度。
class Person: def __init__(self, name, gender): self.name = name self.gender = gender def get_name(self): return self.name def get_gender(self): return self.gender class Male(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Mr." + self.name) class Female(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Miss." + name) if __name__ == '__main__': obj1 = Male('太白','male') obj3 = Male('玮哥', 'male') obj2 = Female('小花', 'female') print(obj1.get_name()) print(obj1.get_gender()) print(obj2.get_gender()) print(obj3.get_gender())
class Person: def __init__(self, name, gender): self.name = name self.gender = gender def get_name(self): return self.name def get_gender(self): return self.gender class Male(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Mr." + self.name) class Female(Person): def __init__(self, name, gender): super().__init__(name, gender) print("Hello Miss." + name) class Factory: def get_person(self, name, gender): # 统一了实例化对象的接口 if gender == 'male': return Male(name, gender) else: return Female(name, gender) if __name__ == '__main__': factory = Factory() # 每次实例化对象时调用统一的接口即可 p1 = factory.get_person("太白", "male") p2 = factory.get_person("小花", "female") p3 = factory.get_person("玮哥", "male") p1.get_name() p1.get_gender() p2.get_name() p2.get_gender() p3.get_name()
建造者模式
将一个复杂对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。在该模式中,有两个参与者:建造者(builder)和指挥者(director)。建造者负责创建复杂对象的各个组成部分,指挥者负责制定对象特定的执行顺序。
示例:
1.有一个接口类,定义创建对象的方法。一个指挥员类,接受创造者对象为参数(组合)。两个创造者类,创建对象方法相同,内部创建可自定义
2.一个指挥员,两个创造者(瘦子 胖子),指挥员可以指定由哪个创造者来创造。
from abc import ABCMeta, abstractmethod class Builder(metaclass=ABCMeta): # 接口类,制定规范,只要此类必须要设置以下的被abstractmethod装饰的这些方法。 @abstractmethod def draw_arm(self): pass @abstractmethod def draw_foot(self): pass @abstractmethod def draw_head(self): pass @abstractmethod def draw_body(self): pass class Thin(Builder): def draw_arm(self): print("画手") def draw_foot(self): print("画脚") def draw_head(self): print("画头") def draw_body(self): print("画瘦身体") class Fat(Builder): def draw_arm(self): print("画手") def draw_foot(self): print("画脚") def draw_head(self): print("画头") def draw_body(self): print("画胖身体") class Director(): def __init__(self, person): self.person = person def draw(self): self.person.draw_arm() self.person.draw_foot() self.person.draw_head() self.person.draw_body() if __name__ == '__main__': thin = Thin() fat = Fat() director_thin = Director(thin) # 组合 director_thin.draw() # 按照指挥者的类的draw方法顺序执行 director_fat = Director(fat) # 组合 director_fat.draw() # 按照指挥者的类的draw方法顺序执行
原型模式
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
原型模式本质是克隆对象,所以在对象初始化操作比较复杂的情况下,很实用,能大大降低耗时,提高性能。因为“不用重新初始化对象,而是动态获得对象运行的状态”。属于副本的概念,与引用不同。
引用就是多个变量共同指向一个数据的内存地址(变量多次赋值)。副本就是重新创建一份。
原型模式示例:某一本书在初版之后又经历了一次改版,对书名、价钱、页码等进行了修改。但是这次修改并不是重新装订了一本书,只是修改了部分内容,这时候就不需要重新创建一个对象,只需要把原来的对象的副本内容做一些修改。
import copy from collections import OrderedDict class Book: def __init__(self, name, authors, price, **rest): '''rest的例子有:出版商、长度、标签、出版日期''' self.name = name self.authors = authors self.price = price # 单位为美元 self.__dict__.update(rest) def __str__(self): return ''.join([f'{k}: {v}\n' if k != "price" else f'{k}: {v}$\n' for k, v in self.__dict__.items()]) class Prototype: def __init__(self): self.objects = dict() def register(self, identifier, obj): self.objects[identifier] = obj def unregister(self, identifier): del self.objects[identifier] def clone(self, identifier, **attr): found = self.objects.get(identifier) if not found: raise ValueError('Incorrect object identifier: {}'.format(identifier)) obj = copy.deepcopy(found) # 深copy一个对象 obj.__dict__.update(attr) return obj def main(): b1 = Book('太白教你学python', ('太白金星', '老司机'), price=118, publisher='IT出版社', publication_date='2014-08-22', tags=('IT', 'programming', 'py', '人工智能')) prototype = Prototype() cid = 'first' prototype.register(cid, b1) b2 = prototype.clone(cid, name='Django框架深度剖析', price=129.9, publication_date='2016-04-01', edition=2) # 通过克隆b1对象的方式创建b2对象,这样避免了实例化对象b1时的复杂操作,提升性能,很实用。 for i in (b1, b2): print(i) print("ID b1 : {} != ID b2 : {}".format(id(b1), id(b2))) if __name__ == '__main__': main()