聊聊 Python 面试最常被问到的几种设计模式(上)

菜鸟学Python

共 6401字,需浏览 13分钟

 · 2021-11-12



1. 前言

在很多人的印象里,Python 作为一款动态编程语言,在日常开发中也很少涉及到设计模式

事实上,任何一个编程语言都可以使用设计模式,它可以保证代码的规范性,只是每一种语言的实现方式略有不同而已

今天我们聊聊 Python 面试中,常被问到的 5 种设计模式,它们是:单例模式、工厂模式、构建者模式、代理模式、观察者模式

2. 单例模式

单例模式,是最简单常用的设计模式,主要目的是保证某一个实例对象只会存在一个,减少资源的消耗

Python 单例模式有很多实现方式,这里推荐下面 2 种

第 1 种,重写 __new__ 方法

定义一个实例变量,在 __new__ 方法中保证这个变量仅仅初始化一次

# 单例模式
class Singleton(object):
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls, *args, **kwargs)
        return cls._instance

使用方式如下:

if __name__ == '__main__':
    # 构建3个实例
    instance1 = Singleton()
    instance2 = Singleton()
    instance3 = Singleton()

    # 打印出实例的内存地址,判断是否是同一个实例
    print(id(instance1))
    print(id(instance2))
    print(id(instance3))

第 2 种,闭包定义装饰器

使用闭包的方式定义一个单例装饰器,将类的定义隐藏到闭包函数中

def singleton(cls):
    """
    定义单例的装饰器(闭包)
    :param cls:
    :return:
    """

    _instance = {}

    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]

    return _singleton

使用上面装饰器的类,构建的实例都能保证单例存在

@singleton
class Singleton(object):
    """单例实例"""

    def __init__(self, arg1):
        self.arg1 = arg1

使用方式如下:

if __name__ == '__main__':
    instance1 = Singleton("xag")
    instance2 = Singleton("xingag")

    print(id(instance1))
    print(id(instance2))

需要注意的是,上面 2 种方式创建的单例并不适用于多线程

要保证多线程中构建的实例对象为单例,需要在 __new__ 函数中使用 threading.Lock() 加入同步锁

class Singleton(object):
    """
    实例化一个对象
    """


     # 锁
    _instance_lock = threading.Lock()

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = object.__new__(cls)
        return Singleton._instance

使用的时候,在线程任务中实例化对象,运行线程即可

def task(arg):
    """
    任务
    :param arg:
    :return:
    """

    instance = Singleton()
    print(id(instance), '\n')

if __name__ == '__main__':
    # 3个线程
    for i in range(3):
        t = threading.Thread(target=task, args=[i, ])
        t.start()

这样,就保证了多线程创建的实例是单例存在的,不会导致脏数据!

3. 工厂模式

生产3种水果对象为例,定义 3 类水果,分别是:苹果、香蕉、橘
# 定义一系列水果
class Apple(object):
    """苹果"""

    def __repr__(self):
        return "苹果"


class Banana(object):
    """香蕉"""

    def __repr__(self):
        return "香蕉"


class Orange(object):
    """橘子"""

    def __repr__(self):
        return "橘子"
工厂模式包含:简单工厂、工厂方法、抽象工厂
第 1 种,简单工厂
简单工厂是最常见的工厂模式,适用于简单的业务场景
首先,定义一个工厂类,创建一个静态方法,根据输入的类型,返回不同的对象
class FactorySimple(object):
    """简单工厂模式"""

    @staticmethod
    def get_fruit(fruit_name):
        if 'a' == fruit_name:
            return Apple()
        elif 'b' == fruit_name:
            return Banana()
        elif 'o' == fruit_name:
            return Orange()
        else:
            return '没有这种水果'
使用方式如下:
if __name__ == '__main__':
    # 分别获取3种水果
    # 输入参数,通过简单工厂,返回对应的实例
    instance_apple = FactorySimple.get_fruit('a')
    instance_banana = FactorySimple.get_fruit('b')
    instance_orange = FactorySimple.get_fruit('o')
第 2 种,工厂方法
工厂方法将创建对象的工作让相应的工厂子类去实现,保证在新增工厂类时,不用修改原有代码
首先,创建一个抽象公共工厂类,并定义一个生产对象的方法
import abc
from factory.fruit import *

class AbstractFactory(object):
    """抽象工厂"""
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def get_fruit(self):
        pass
接着,创建抽象工厂类的 3 个子类,并重写方法,创建一个实例对象并返回
class AppleFactory(AbstractFactory):
    """生产苹果"""

    def get_fruit(self):
        return Apple()

class BananaFactory(AbstractFactory):
    """生产香蕉"""

    def get_fruit(self):
        return Banana()

class OrangeFactory(AbstractFactory):
    """生产橘子"""

    def get_fruit(self):
        return Orange()
最后的使用方式如下:
if __name__ == '__main__':
    # 每个工厂负责生产自己的产品也避免了我们在新增产品时需要修改工厂的代码,而只要增加相应的工厂即可
    instance_apple = AppleFactory().get_fruit()
    instance_banana = BananaFactory().get_fruit()
    instance_orange = OrangeFactory().get_fruit()

    print(instance_apple)
    print(instance_banana)
    print(instance_orange)
第 3 种,抽象工厂
如果一个工厂要生产多个产品,使用工厂方法的话,就需要编写很多工厂类,不太实用,使用抽象工厂就可以很好的解决这个问题
以川菜馆和湘菜馆炒两个菜,毛血旺和小炒肉为例
首先,创建川菜毛血旺、川菜小炒肉、湘菜毛血旺、湘菜小炒肉 4 个类
class MaoXW_CC(object):
    """川菜-毛血旺"""

    def __str__(self):
        return "川菜-毛血旺"

class XiaoCR_CC(object):
    """川菜-小炒肉"""

    def __str__(self):
        return "川菜-小炒肉"

class MaoXW_XC(object):
    """湘菜-毛血旺"""

    def __str__(self):
        return "湘菜-毛血旺"

class XiaoCR_XC(object):
    """湘菜-小炒肉"""

    def __str__(self):
        return "湘菜-小炒肉"
然后,定义一个抽象工厂类,内部定义两个方法,可以生成毛血旺和小炒肉
class AbstractFactory(object):
    """
    抽象工厂
    既可以生产毛血旺,也可以生成小炒肉
    """

    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def product_maoxw(self):
        pass

    @abc.abstractmethod
    def product_xiaocr(self):
        pass
接着,创建抽象工厂类的两个子类,川菜工厂和湘菜工厂,重写方法,然后创建对应的实例对象返回
class CCFactory(AbstractFactory):
    """川菜馆"""

    def product_maoxw(self):
        return MaoXW_CC()

    def product_xiaocr(self):
        return XiaoCR_CC()


class XCFactory(AbstractFactory):
    """湘菜馆"""

    def product_maoxw(self):
        return MaoXW_XC()

    def product_xiaocr(self):
        return XiaoCR_XC()
最后,使用川菜工厂和湘菜工厂分别炒两个菜
if __name__ == '__main__':
    # 川菜炒两个菜,分别是:毛血旺和小炒肉
    maoxw_cc = CCFactory().product_maoxw()
    xiaocr_cc = CCFactory().product_xiaocr()

    print(maoxw_cc, xiaocr_cc)

    maoxw_xc = XCFactory().product_maoxw()
    xiaocr_xc = XCFactory().product_xiaocr()

    print(maoxw_xc, xiaocr_xc)

4. 最后

单例模式和工厂模式是日常使用最为频繁的两种设计模式,下篇文章将聊聊后面 3 种设计模式,希望大家在留言区捧个场支持一下,月底统计常来留言的同学送书,给个三连哈!你的支持,就是我原创的最佳动力!



大神来教大家玩服务器:
安老师,某大厂互联网公司的资深后端大神,有5年多的后端开发经验,教大家玩转云服务器玩Linux,安装Python,安装数据库,用Django+uwsigi+nginx web服务搭建网站,求职简历网站搭建,写脚本部署股票基金机器人,还可以搭建个人网盘!

星球的价格是一年88元,然后现在双11期间有优惠券20元,相当于是68一年,算下来每个月6块钱,连半杯奶茶的钱都不到,一天才2毛钱,非常划算。





推荐阅读:

入门: 最全的零基础学Python的问题  | 零基础学了8个月的Python  | 实战项目 |学Python就是这条捷径


干货:爬取豆瓣短评,电影《后来的我们》 | 38年NBA最佳球员分析 |   从万众期待到口碑扑街!唐探3令人失望  | 笑看新倚天屠龙记 | 灯谜答题王 |用Python做个海量小姐姐素描图 |碟中谍这么火,我用机器学习做个迷你推荐系统电影


趣味:弹球游戏  | 九宫格  | 漂亮的花 | 两百行Python《天天酷跑》游戏!


AI: 会做诗的机器人 | 给图片上色 | 预测收入 | 碟中谍这么火,我用机器学习做个迷你推荐系统电影


小工具: Pdf转Word,轻松搞定表格和水印! | 一键把html网页保存为pdf!|  再见PDF提取收费! | 用90行代码打造最强PDF转换器,word、PPT、excel、markdown、html一键转换 | 制作一款钉钉低价机票提示器! |60行代码做了一个语音壁纸切换器天天看小姐姐!


年度爆款文案

浏览 13
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报