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

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 种设计模式

(0)

相关推荐

  • 感觉自己的代码很乱?因为你不懂套路

    编程教室开了这么久,已经有很多人从完全零基础的小白成为了会写代码的菜鸟程序员,能够自己独立开发程序.不过到此阶段,常常会遇到瓶颈,感觉功能可以实现,但代码看起来有些别扭: 代码中有很多相似的重复代码 ...

  • Python中的单例模式有几种实现方式?

    公众号新增加了一个栏目,就是每天给大家解答一道Python常见的面试题,反正每天不贪多,一天一题,正好合适,只希望这个面试栏目,给那些正在准备面试的同学,提供一点点帮助! 小猿会从最基础的面试题开始, ...

  • Python实现单例模式的5种方式

    写在前面 学究嘛,就记录一下; 本质都是通过设置一个标志位来实现, 通俗的讲就是当第一次实例化时, 记录下"已经实例化了", 当再次实例化时, 将"记录"的地址 ...

  • 前端设计模式总结

    菜鸟看书的一点总结,请大佬给出宝贵意见 创建型 工厂模式 创建对象的工厂,使用者不必关心对象生成的过程,也就是不需要显示的调用new 操作符,只需要调用对象工厂暴露出来的创建对象的方法,并传入需要创建 ...

  • Java面试题总结之OOA/D,UML,和XML

    全文字数:   2732 阅读时间:   大约9 分钟 1.UML 是什么?常用的几种UML图? 统一建模语言(Unified Modeling Language,UML)又称标准建模语言:常用图包括 ...

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

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

  • Python面试1-65题汇总,都是常问的面试题!

    周末,Python面试题每日一题暂停更新,下面把最近整理的1-50篇Python面试文整理一下,平时文章都放在比较末尾,阅读量都不高,相信很多人都没看过,如果对于Python感兴趣的,建议可以认真阅读 ...

  • 面试官常问的“一致性哈希”,都在这 18 张图里

    大家好,好久不见啦.最近快年底了,公司.部门事情太多:冲刺 KPI.做部门预算--所以忙东忙西的,写文章就被耽搁了.再加上这篇文章比较硬,我想给大家讲得通俗易懂,着实花了很多时间琢磨怎么写. 话不多说 ...

  • android颜色!那些年Android面试官常问的知识点,大厂面经合集

    为什么想跳槽? 简单说一下当时的状况,我在这家公司做了两年多,这两年多完成了一个大项目,作为开发的核心主力,开发压力很大,特别是项目上线前的几个月是非常辛苦,几乎每晚都要加班到12点以后,周末最多只有 ...

  • 一文彻底搞懂面试中常问的各种“锁”

    前言 锁,顾名思义就是锁住一些资源,当只有我们拿到钥匙的时候,才能操作锁住的资源.在我们的Java,数据库,还有一些分布式的环境中,总是充斥着各种各样的锁让人头疼,例如"公平锁". ...

  • 最全总结 | 聊聊 Python 办公自动化之 Word(下)

    最全总结 | 聊聊 Python 办公自动化之 Word(下)

  • 常有人问这个病怎么办?有没有其它方法?

    视频转自原始点医学网,根据公众号转载原则转载,推广内容与本公众号无关. 文中所涉及到内容等仅供专业人士参考,不能作为处方:仅供参考,请勿盲目试用,本平台不承担由此产生的任何责任! 推荐以下原始点最全视 ...

  • 面试官为什么要问你父母的工作?谨记这3点原因,不懂别乱回答

    我们都知道,面试是进入职场的第一关,如果连这里都过不去,那么其他的就更不用谈了.在面试的时候,一定要注意细节,很多话不该说的就不要说.俗话说得好,言多必失,如果不谨言慎行,很可能就会被淘汰,失去机会. ...

  • 常有人问这个病原始点能治吗?

    常有人问这个病原始点能治吗?