- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
接触Python比较早的朋友可能都有这样的体会,Python语言虽然也支持面向对象的编程方式, 。
但是,不像那些纯面向对象的语言(比如Java和.NET)那样严格和规范.
随着项目的规模逐步扩大之后,想要以一种清晰、可维护和可扩展的方式定义和实施对象的行为就变得越来越困难.
今天介绍的Python中两个为面向对象编程提供的强大工具:接口和抽象基类.
它们的英文分别为Protocols和ABC(Abstract Base Classes).
Protocols是Python3.8才开始引入的,有的地方也翻译成协议,我感觉翻译成接口更熟悉一些.
ABC引入的比较早,在Python3之后得到了改进和优化,现在和其他语言的抽象类相比,差别不大.
Python3.8开始在类型模块中引入的接口Protocols的概念,它提供了一种无需显式继承即可定义接口的方法.
接口Protocols定义了一组方法或属性,只要一个对象实现了这些方法或属性,就被视为满足该接口.
下面通过一个示例来帮助理解Protocols的使用,如果有面向对象编程的经验,很容易就能理解这个概念.
这个示例来自最近用的一个量化交易系统的一部分,这个功能需要从不同的来源获取数据,然后进行分析,最后将分析结果以不同的方式输出.
这三个步骤(获取数据,分析和输出)中, 。
假设获取数据的来源有网络(API),文件(CSV)和数据库3种; 。
分析的步骤是统一的;输出的方式假设也有多种,比如邮件,短信等等.
根据这个描述,使用Protocols构建的获取数据和分析部分的代码如下:
(输出的部分暂时不管) 。
from typing import Protocol
# 输入数据的接口
class InputData(Protocol):
def get_data(self) -> str:
pass
class APIHandler:
def get_data(self) -> str:
print("get_data from API")
return "get data from API"
class CSVHandler:
def get_data(self) -> str:
print("get_data from CSV")
return "get data from CSV"
class SqliteHandler:
def get_data(self) -> str:
print("get_data from SQLITE DATABASE")
return "get data from SQLITE DATABASE"
# 分析数据
def analysis(i: InputData):
data = i.get_data()
print("开始处理数据...")
InputData继承了Protocol,其中定义了接口的函数get_data.
只要实现了get_data的class,比如APIHandler,CSVHandler和SqliteHandler,都可以当做InputData类型.
从代码可以看出,我们不需要用APIHandler去继承InputData,只要实现InputData中的所有方法就可以了.
这种灵活性确保了系统的可扩展性,我们可以添加新的数据源类型,而无需修改现有代码.
接下来我们测试上面的代码是否可以正常使用:
if __name__ == "__main__":
i = APIHandler()
analysis(i)
print("\n")
i = CSVHandler()
analysis(i)
print("\n")
i = SqliteHandler()
analysis(i)
print("\n")
运行结果:
$ python.exe .\protocol_abc.py
get_data from API
开始处理数据...
get_data from CSV
开始处理数据...
get_data from SQLITE DATABASE
开始处理数据...
Protocol非常具有灵活性,但有时我们需要更结构化的方法,这就是抽象基类 (ABC) 的用武之地.
ABC 是一种通过定义子类必须实现的严格接口来强制执行一致行为的工具.
与Protocol不同,ABC 需要显式继承,因此当我们希望在代码中明确定义层次结构时,ABC是更好的选择.
接着实现上一节示例中的输出部分,每种不同的输出需要不同的配置, 。
比如输出到邮件需要先配置邮箱的账号信息,输出到短信需要配置手机信息等等.
在这里,我们使用 ABC 来实现输出的基类.
# 输出的抽象基类
class OutputResult(ABC):
def __init__(self):
self.settings: dict = {}
@abstractmethod
def send(self, data: str):
pass
@abstractmethod
def config(self, settings: dict):
pass
class OutputMail(ABC):
def send(self, data: str):
print(f"send {data} to {self.settings['name']}")
def config(self, settings: dict):
self.settings = settings
class OutputMessage(ABC):
def send(self, data: str):
print(f"send {data} to {self.settings['name']}")
def config(self, settings: dict):
self.settings = settings
这里使用抽象基类的原因是输出时,并不是简单的调用send方法就可以的,还需要配置输出的参数, 。
所以用带有结构的抽象基类更好.
加上输出之后,上一节中的分析函数也改为:
# 分析数据
def analysis(i: InputData, o: OutputResult):
data = i.get_data()
print("开始处理数据...")
data = data.replace("get data from ", "")
o.send(data)
测试的代码如下:
if __name__ == "__main__":
i = APIHandler()
o = OutputMail()
o.config({"name": "aaa@bbb.com"})
analysis(i, o)
print("\n")
i = CSVHandler()
o = OutputMessage()
o.config({"name": "13911123456"})
analysis(i, o)
print("\n")
i = SqliteHandler()
o = OutputMail()
o.config({"name": "xyz@www.com"})
analysis(i, o)
print("\n")
运行结果:
$ python.exe .\protocol_abc.py
get_data from API
开始处理数据...
send API to aaa@bbb.com
get_data from CSV
开始处理数据...
send CSV to 13911123456
get_data from SQLITE DATABASE
开始处理数据...
send SQLITE DATABASE to xyz@www.com
当我们在实际的开发设计中,应该如何选择Protocol和ABC呢?
其实,Protocol和ABC之间的选择并不是非黑即白,这通常取决于项目的背景和你的目标.
一般来说,下面这些情况我们优先选择使用Protocol:
而下面这些情况,优先选择ABC:
总的来说,Protocol和ABC不是互相竞争的两种工具,它们是互补的.
我使用Protocol将类型安全改造到遗留系统中,而不需要大量重构.
另一方面,如果我在从头开始构建一个结构和一致性至关重要的系统时,会使用 ABC.
在决定使用哪个时,请考虑项目的灵活性需求和长期目标.
Protocol提供灵活性和无缝集成,而 ABC 有助于建立结构和一致性.
通过了解它们各自的优势,你可以选择合适的方式来构建健壮、可维护的 Python 系统.
最后此篇关于谈谈Python中的接口与抽象基类的文章就讲到这里了,如果你想了解更多关于谈谈Python中的接口与抽象基类的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我对 java 中的抽象有点困惑。 我查了很多页面说抽象就是数据隐藏(隐藏实现)。 我对抽象的理解是它是“部分实现”。只需在抽象类/接口(interface)中定义您需要的内容,然后扩展/实现它们并添
我是 Ruby 的新手,主要来自 C# 和 ActionScript 3(以及其他语言)。我对抽象功能很好奇。具体来说,包装和抽象 Ruby 的 FTP 和 SFTP 库。 我四处搜索,发现了一个名为
目录 Java基础知识(抽象) 抽象 抽象定义 abstract的使用 定义抽象类
这个月我花了一些时间与 Emacs Lisp 进行斗争,试图获得更好地满足我需求的自动缩进。令人惊讶的是,大多数缩进代码是多么低级。我只看到了很少的抽象,例如 搜索不在字符串或注释中的第一个正则表达式
我有以下内容: public abstract class Foo{ //contents of Foo // ... public class Bar extends
我有三个类(class)(A 类、B 类和 C 类)。 类 A 调用 B 的实例并运行 start()。 B类扩展了Thread,因此当调用start()时,run()方法中的任何内容都会被执行。 在
这个问题已经有答案了: Calling a subclass method from superclass (5 个回答) 已关闭 7 年前。 Klasse1是一个抽象类,有一个 foo()方法。 K
我有一个这样的函数: def test(): x = "3" # In actual code, this is computed if x is None: retu
我有两个基类之间的关系: public abstract class RecruiterBase { // Properties declare here // Constructors de
这是我第一次发帖,但我遇到了很多问题。我目前有一个带有标题的 AbstractDevice 类: public abstract class AbstractDevice> implements De
我有一个 MotorDefinition 类和一个名为 Motor 的抽象类: class MotorDefinition { public: MotorDefinition(int p1,
是否有任何方法可以在这种代码(sass)中制定 css 的抽象规则: #cottage-image-gallery input:nth-of-type(1):checked ~ label:nth-o
是否可以声明一个已知的基类型并允许传输所有派生类型? [ServiceContract] public interface IService { [OperationContract]
我目前正在为基于 Java 的文本游戏开发角色生成机制,但我遇到了一个问题,看不出哪里出了问题。我有一个“Character”类,它是抽象的,然后是另一个类“NPCharacter”,它是建立在这个之
抱歉,标题令人困惑。不太确定如何表达它,这可能是问题所在! 我正在寻找一个好的抽象来用于涉及并发线程的情况。 我已经接近了,但还不是很清楚。 稍微简化一下,我在 Android 手机上收集了两种传感器
提前感谢您阅读本文。我不完全理解如何/何时使用摘要,所以我试图在我从事的每个项目中考虑它,看看它是否会在某一天全部点击 Smile | :) 此外,可访问性级别(私有(private)、 protec
我正在探索用于生成 Web 内容的 XML -> XSLT -> HTML 模因。我的 XSLT 经验很少。 我很好奇 XSLT 中有哪些机制可用于处理抽象或“重构”。 例如,使用通用 HTML 和服
在这些谈话中 Nicholas Zakas和 Addy Osmani他们讨论了在构建大型 Javascript 应用程序时将外观模式用作沙箱的想法,以便将应用程序与底层基础库分离。 这种解耦理论上允许
我使用C++和CUDA/C,想为特定问题编写代码,但遇到了一个非常棘手的简化问题。 我在并行编程方面的经验不容忽视,但相当有限,我无法完全预见到此问题的特殊性。 我怀疑是否有一种方便甚至“轻松”的方式
假设我有: trait A class B extends A class C extends A 有没有办法配置类型参数: class Foo[AType <: A with canCreateIn
我是一名优秀的程序员,十分优秀!