python的设计模式

  • Post author:
  • Post category:python




设计模式


设计模式

是一套

被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结

。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。



0、接口

定义:

一种特殊的类,声明了若干方法,要求继承该接口的类必须实现这种方法,

作用:限制继承接口的类的方法的名称及调用方式,隐藏了类的内部实现
请添加图片描述



1、单例模式

​ 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是

确保某一个类只有一个实例存在



在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场



定义:

保证一个类只有一个实例,并提供一个访问它的全局访问点


适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。


优点



1、在内存里只有一个实例,减少了内存的开销

,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。


2、避免对资源的多重占用

(比如写文件操作)。

比如,

某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息

。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。



方法一:使用模块

实现方法:将需要实现的单例功能放到一个.py 文件中

实现原理:

Python 的模块就是天然的单例模式



因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码

。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

mysingleton.py
class Singleton(object):
    def foo(self):
        pass
singleton = Singleton()

将上面的代码保存在文件 mysingleton.py 中,要使用时,直接在其他文件中导入此文件中的对象,这个对象即是单例模式的对象

from a import singleton


方法二、装饰器实现
def Singleton(cls):
    _instance = {}
    def _singleton(*args, **kargs):
        if cls not in _instance:#判断该实例是否存在,存在就直接返回,不存在就创建        
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    return _singleton
 
@Singleton
class A(object):
    a = 1
    def __init__(self, x=0):
        self.x = x

# a1 = A(2)
a2 = A(3)
# 输出:{<class '__main__.A'>: <__main__.A object at 0x7fb9af751af0>}


方法三、基于__new__方法

我们知道,

当我们实例化一个对象时,是先执行了类的__new__方法

(我们没写时,默认调用object.

new

),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式

个人最初常用的是重写__new__方法的方式,但是用重写类中的__new__方法,在多次创建对象时,尽管返回的都是同一个对象,但是每次执行创建对象语句时,内部的__init__方法都会被自动调用,而在某些应用场景,可能存在初始化方法只能允许运行一次的需求,这时这种实现单例的方式并不可取

class Download(object):
    instance = None

    def __init__(self):
        print("__init__")
    
    def __new__(cls, *args, **kwargs):
        if cls.instance is None:
            cls.instance = super().__new__(cls)
        return cls.instance

object1 = Download()
object2 = Download()
print(object1)
print(object2)

运行结果,可以看到初始化方法多次执行了:

__init__
__init__
<__main__.Download object at 0x10268a748>
<__main__.Download object at 0x10268a748>

原文链接:https://blog.csdn.net/weixin_43783714/article/details/103352436


init 方法通常用在初始化一个类实例时候,但其实它不是实例化一个类的时候第一个被调用 的方法

。当使用 Student(id, name) 这样的表达式来实例化一个类时,

最先被调用的方法 其实是 new 方法。

new方法接受的参数虽然也是和init一样,但init是在类实例创建之后调用,

而 new方法正是创建这个类实例的方法


new为对象分配空间,是内置的静态方法,new在内存中为对象分配了空间也返回了对象的引用,init获得了这个引用才初始化这个实例。

示例

一个非常简单的单例

class A:
 instance = None
 def __new__(cls, *args, **kwargs):
  if cls.instance is None:
   cls.instance = super().__new__(cls)
  return cls.instance

因为new方法是一个静态方法(也就是在定义的时候就没有cls参数),所以在这里要传入一个cls参数,

而且这里的new你改造过了,所以要返回爸爸的new方法

按造这个方法改造的单例怎么new都是同一个实例,

但init仍然会被执行多次,也就是创建了几个对象就调用几次初始化方法。所以还要对init再进行一些判断。

class A:
 instance = None
 init_flag = False # 初始化标记
 
 def __new__(cls, *args, **kwargs):
  if cls.instance is None:
   cls.instance = super().__new__(cls)
  return cls.instance
 
 def __init__(self):
  if A.init_flag:
   return
  print('执行了初始化方法')
  A.init_flag = True
 
if __name__ == '__main__':
 a = A()
 b = A()
 print(a)
 print(b)



2、工厂模式


概念

定义一个创建对象的接口,

但让实现这个接口的类来决定实例化哪个类

。工厂方法让类的实例化推迟到子类中进行。属于创建型模式,它提供了一种创建对象的最佳方式。目标是当直接创建对象(在Python中是通过__init__()函数实现的)不太方便时,提供更好的方式。

在工厂设计模式中,客户端①可以请求一个对象,而无需知道这个对象来自哪里;也就是,使用哪个类来生成这个对象。工厂背后的思想是

简化对象的创建

。与客户端自己基于类实例化直接创建对象相比,基于一个中心化函数来实现,更易于追踪创建了哪些对象。


通过将创建对象的代码和使用对象的代码解耦,工厂能够降低应用维护的复杂度。

工厂方法创建对象时,我们并没有与某个特定类耦合/绑定到一起,而只是通过调用某个函数来提供关于我们想要什么的部分信息。这意味着修改这个函数比较容易,不需要同时修改使用这个函数的代码。

工厂通常有两种形式:


第一种是工厂方法(Factory Method)

,它是一个方法(或以Python术语来说,是一个函数),对不同的输入参数返回不同的对象;


第二种是抽象工厂

,它是一组用于创建一系列相关事物对象的工厂方法。

两种形式的区别:

https://www.cnblogs.com/Zzbj/p/15778464.html

在工厂方法(简单工厂)模式中,我们执行单个函数,传入一个参数(提供信息表明我们想要什么),但并不要求知道任何关于对象如何实现以及对象来自哪里的细节。


一个例子:

我们有一个基类Person ,包涵获取名字,性别的方法 。有两个子类male 和female,可以打招呼。还有一个工厂类。工厂类有一个方法名getPerson有两个输入参数,名字和性别。用户使用工厂类,通过调用getPerson方法。


实现一个工厂方法,通过输入物料,然后产出不同的产品类。在程序运行期间,用户传递性别给工厂

,工厂创建一个与性别有关的对象。因此工厂类在运行期,决定了哪个对象应该被创建。

class Person:
    def __init__(self):
        self.name = None
        self.gender = None

    def getName(self):
        return self.name
     
    def getGender(self):
        return self.gender

class Male(Person):
    def __init__(self, name):
        print "Hello Mr." + name

class Female(Person):
    def __init__(self, name):
        print "Hello Miss." + name

class Factory:
    def getPerson(self, name, gender):
        if gender == ‘M':
            return Male(name)
        if gender == 'F':
            return Female(name)
if __name__ == '__main__':
    factory = Factory()
    person = factory.getPerson("Chetan", "M")



3、建造者模式

​ 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式将所有细节都交由子类实现。需求,画人物,要求画一个人的头,左手,右手,左脚,右脚和身体,画一个瘦子,一个胖子


不使用设计模式:

if __name__=='__name__':
    print '画左手'
    print '画右手'
    print '画左脚'
    print '画右脚'
    print '画胖身体'

    print '画左手'
    print '画右手'
    print '画左脚'
    print '画右脚'
    print '画瘦身体'

​ 这样写的缺点每画一个人,都要依次得画他的六个部位,这些部位有一些事可以重用的,所以调用起来会比较繁琐,而且客户调用的时候可能会忘记画其中的一个部位,所以容易出错。



建造一个抽象的类Builder,声明画六个部位的方法,每画一种人,就新建一个继承Builder的类,这样新建的类就必须要实现Builder的所有方法

,这里主要运用了抽象方法的特性,父类定义了几个抽象的方法,子类必须要实现这些方法,否则就报错,这里解决了会漏画一个部位的问题。建造一个指挥者类Director,输入一个Builder类,定义一个draw的方法,把画这六个部位的方法调用都放在里面,这样调用起来就不会繁琐了。

Python本身不提供抽象类和接口机制,要想实现抽象类,可以借助abc模块。abc是Abstract Base Class的缩写。

被@abstractmethod装饰为抽象方法后,该方法不能被实例化;除非子类实现了基类的抽象方法,所以能实例化。

#encoding=utf-8
from abc import ABCMeta, abstractmethod
class Builder():
    __metaclass__ = ABCMeta

    @abstractmethod
    def draw_left_arm(self):
        pass
     
    @abstractmethod
    def draw_right_arm(self):
        pass
     
    @abstractmethod
    def draw_left_foot(self):
        pass
     
    @abstractmethod
    def draw_right_foot(self):
        pass
     
    @abstractmethod
    def draw_head(self):
        pass
     
    @abstractmethod
    def draw_body(self):
        pass

class Thin(Builder):#继承抽象类,必须实现其中定义的方法
    def draw_left_arm(self):
        print '画左手'

    def draw_right_arm(self):
        print '画右手'
     
    def draw_left_foot(self):
        print '画左脚'
     
    def draw_right_foot(self):
        print '画右脚'
     
    def draw_head(self):
        print '画头'
     
    def draw_body(self):
        print '画瘦身体'



class Fat(Builder):
    def draw_left_arm(self):
        print '画左手'

    def draw_right_arm(self):
        print '画右手'
     
    def draw_left_foot(self):
        print '画左脚'
     
    def draw_right_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_left_arm()
        self.person.draw_right_arm()
        self.person.draw_left_foot()
        self.person.draw_right_foot()
        self.person.draw_head()
        self.person.draw_body()

if __name__=='__main__':
    thin=Thin()
    fat=Fat()
    director_thin=Director(thin)
    director_thin.draw()
    director_fat=Director(fat)
    director_fat.draw()

建造者模式用于将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。