python 有什么高阶玩法?

123 篇文章 1 订阅
23 篇文章 1 订阅

前言

同样实现一项功能,不同的编码方式千差万别。优秀的代码,在可读性、维护等方面可以把成本降至最低。而劣质的代码,会给后期维护带来灾难性的时间消耗,而且会存在很多隐性隐患。

Python也是这样,通过使用简单的条件、循环语句,进行一些逻辑串联可以实现我们想要的功能,但是,如果你掌握了Python更加高级的用法,那么将会大大提升代码的质量。

本文,就来给大家介绍一下Python抽象基类和工厂模式,以及它的的优势所在。

假如,你现在需要实现2个函数uploaddownload实现数据存储功能,分别用于数据的上传和下载,简单的代码示例如下:

# 上传数据
upload(data, path_to_upload)

# 下载数据
data = download(path_to_download)

早期开发项目可以这样实现,但是,后期随着版本的不断迭代,先前实现的版本已经无法满足新的需求,你要实现一个新版本的上传、下载功能,但是,偶尔还会用到老版本的功能,这时候你就需要把老版本的副本保留下来,另外实现一个新版本的功能。然后,通过传递版本号来判断到底要调用哪个版本的功能。

img

简单的方法

前面介绍了一下背景,如果使用最为基础的方法,可以这样实现:

# 上传数据
if version == "old":
  upload(data, path_to_upload)
elif version == "new":
  new_upload(data, path_to_upload)
else:
  raise NotImplementedError(f"{version=} not supported")

# 下载数据
if version == "old":
  data = download(path_to_download)
elif version == "new":
  data = new_download(path_to_downlaoad)
else:
  raise NotImplementedError(f"{version=} not supported")

这看上去非常基础,而且非常”不好看“,而且从代码的结构方面也非常糟糕,通过if-else条件语句来判断到底执行哪个版本,试想一下,如果后续增加的版本更多,这样针对上传、下载都要重复写很多if-elif-else语句,非常繁琐。

面向对象

我们可以对把每个文件处理程序定义为一个类,这样的话执行过程要简单一些:

class FileHandler:
  def download(self, path):
    ...

  def upload(self, data, path):
    ...

class NewFileHandler:
  def download(self, path):
    ...

  def upload(self, data, path):
    ...

这时候,我们在调用不同版本时可以这样使用:

if version == "old":
  handler = FileHandler()
elif version == "new":
  handler = NewFileHandler()

# 上传数据
handler.upload(data, path_to_upload)

# 下载数据
data = handler.download(path_to_download)

这样看上去就简单了很多,因为,不需要针对上传和下载都要多写很多if-else语句,也不用去区分到底是哪个版本,只需要首先声明handler然后执行对应的方法就行。

但是,这样还不够完善,因为,在后一个开发人员实现一个新的类时,需要建立在他对先前的代码信息有一定的了解基础之上,他明确的知道要实现uplodadownload这些函数,而没有什么约束,这显然是不合理的。

img

抽象基类

为了解决这个问题,可应用抽象基类,首先定义一个接口,然后后续每次要新添加类的时候都要继承这个接口,这样的话,可以去约束必须实现uploaddownload方法。

from abc import ABC, abstractmethod

class FileHandlerInterface(ABC)
  @abstractmethod
  def download(self, path):
    raise NotImplementedError

  @abstractmethod
  def upload(self, data, path):
    raise NotImplementedError

定义了抽象基类,后续继承它的每个类都要实现执行的抽象方法:

class FileHandler(FileHandlerInterface):
  def download(self, path):
    ...

  def upload(self, data, path):
    ...

class NewFileHandler(FileHandlerInterface):
  def download(self, path):
    ...

  def upload(self, data, path):
    ...

这样做的好处是,能够保障实现必要的方法,避免遗漏关键方法而造成的错误。

协议和静态分析

约束后续实现的方法,使用@abstractmethod只是其中一种。自从Python 3.5加入静态分析之后,我们可以使用更为简单、简洁的方法实现抽象基类能够完成的功能。

PEP 544引入了用于结构子类型化的协议,协议能够替代抽象基类,在运行时提供静态检查。

首先,定义如下协议:

from typing import Protocol

class Handler(Protocol):
  def download(self, path: str) -> bytes:
    raise NotImplementedError

  def upload(self, data: bytes, path: str):
    raise NotImplementedError

这样,不需要再去集成协议,

handler: Handler

if version == "old":
  handler = FileHandler()
elif version == "new":
  handler = NewFileHandler()

只需要把代码中加入handler:Handler声明一下即可,这告诉静态分析器该处理程序是一个处理程序协议,对其进行的任何其他分配都必须与该处理程序协议匹配,如果不符合这个约定,程序则会报错。

工厂模式

到此为止,功能已经完善了很多,但是,区分不同版本仍然需要if-else条件语句来判断,这里,使用工厂模式可以把这一繁琐之处也解决掉。

class FileHandler(Handler):
  version = "old"

  def download(self, path):
    ...

  def upload(self, data, path):
    ...

class NewFileHandler(Handler):
  version = "new"

  def download(self, path):
    ...

  def upload(self, data, path):
    ...

在这段代码中,我们给每个版本实现的类注明它的版本号,然后通过工厂模式来返回对应的实例:

def make_handler(version):
  try:
    handler = next(filter(lambda P: P.version == version, Handlers.__subclasses__()))()
  except StopIteration as err:
    raise NotImplementedError from err

在这段代码中,匿名函数中首先判断了我们想要调用的版本号,然后返回该版本号对应的实例,这样代码看上去就更加简洁了。

元编程与装饰器

除了上述直接把版本号确定性的写在代码里,我们还可以使用元编程结合装饰器,更加灵活的定义每个类的版本:

class HandlerFactory:
  handlers = {}

  @classmethod
  def make_handler(cls, version):
    try:
      retval = cls.handlers[version]
    except KeyError as err:
      raise NotImplementedError(f"{version=} doesn't exist") from err
    return retval

  @classmethod
  def register(cls, type_name):
    def deco(deco_cls):
      cls.handlers[type_name] = deco_cls
      return deco_cls
    return deco


@HandlerFactory.register('old')
class FileHandler(Handler):
  def download(self, path):
    ...

  def upload(self, data, path):
    ...


@HandlerFactory.register('new')
class NewFileHandler(Handler):
  def download(self, path):
    ...

  def upload(self, data, path):
    ...

这样,在使用过程中只需要传递版本号,把选择到底执行哪个对象的问题就抛给了HandlerFactory,而且能够通过装饰器很轻松的更改和注册每个对象的版本号,这样实现更加高效、灵活、简洁,也很便于后期的扩展。

-END-


Python入门教程

如果大家对python感兴趣,下面我会给大家免费分享一份Python全套学习资料, 包含视频、源码、课件,希望能帮到那些不满现状,想提升自己却又没有方向的朋友,可以和我一起来学习交流。

① Python所有方向的学习路线图,清楚各个方向要学什么东西

② 600多节Python课程视频,涵盖必备基础、爬虫和数据分析

③ 100多个Python实战案例,含50个超大型项目详解,学习不再是只会理论

④ 20款主流手游迫解 爬虫手游逆行迫解教程包

爬虫与反爬虫攻防教程包,含15个大型网站迫解

爬虫APP逆向实战教程包,含45项绝密技术详解

⑦ 超300本Python电子好书,从入门到高阶应有尽有

⑧ 华为出品独家Python漫画教程,手机也能学习

⑨ 历年互联网企业Python面试真题,复习时非常方便

在这里插入图片描述

👉Python学习视频600合集👈

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

👉实战案例👈

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
在这里插入图片描述

👉100道Python练习题👈

检查学习结果。

👉面试刷题👈

在这里插入图片描述

资料领取

这份完整版的Python全套学习资料已经上传网盘,朋友们如果需要可以点击下方微信卡片免费领取 ↓↓↓【保证100%免费】
或者

点此链接】领取

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值