高级python需要掌握哪些_Python 开发中有哪些高级技巧?

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

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

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

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

# 上传数据

upload(data, path_to_upload)

# 下载数据

data = download(path_to_download)

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

简单的方法

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

# 上传数据

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然后执行对应的方法就行。

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

抽象基类

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

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,而且能够通过装饰器很轻松的更改和注册每个对象的版本号,这样实现更加高效、灵活、简洁,也很便于后期的扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值