关键时刻,第一时间送达!

作者ANDREI BOYANOV - 高级软件工程师 @TOPTAL

让我们再重申一下:Python是一种具有动态类型和动态绑定的高级编程语言。我将它描述为一个强大的高级动态语言。许多开发人员都喜欢Python,因为它具有清晰的语法、结构良好的模块和包,以及巨大的灵活性和广泛的现代功能。

在Python中,并不要求您写的类和对象进行实例化。如果您的项目中不需要复杂的结构,则可以只编写函数。更好的是,您可以编写一个平面脚本来执行一些简单而快速的任务,而不需要构建代码。

与此同时,Python是面向对象的100%语言。那是什么意思呢?简单地说,Python中的一切都是一个对象。 函数是对象,第一类对象(无论什么意思)。关于函数是对象的这个事实很重要,所以请记住它。

所以,您可以在Python中编写简单的脚本,或者只是打开Python终端,并在那里执行语句(这非常有用!)。但同时,您可以创建复杂的框架、应用程序、库等。您可以在Python中做这么多。当然还有一些限制,但这不是本文的主题。

然而,由于Python是如此强大和灵活,我们在编程时需要一些规则(或模式)。所以,让我们看看什么是模式,以及它们与Python的关系。我们还将着手实施一些基本的Python设计模式。

为什么Python对模式有好处?

任何编程语言对模式都有益。实际上,应该在任何给定的编程语言的上下文中考虑模式。模式,语言语法和类型都对我们的编程产生了限制。来自语言语法和语言本质(动态,功能,面向对象等)的限制可能与其背后存在的原因不同。模式的限制是有原因的,它们是有目的的。这是模式的基本目标; 告诉我们如何做到某事,以及怎么样会做不到。稍后我们将讨论模式,特别是Python设计模式。

Python是一种动态和灵活的语言。Python设计模式是利用其巨大潜力的好方法。

Python的理念建立在思想最好的做法之上。Python是一种动态语言(我已经说过了吗?),因此,已经实现了一些流行的设计模式,其中包含几行代码。一些设计模式是内置在Python中,所以即使不知道,我们也可以使用它们。由于语言的本质,不需要其他模式。

例如,Factory是一种结构化的Python设计模式,其旨在创建新对象,并在用户那儿隐藏实例化逻辑。但是在Python中创建对象是动态的,所以不需要像Factory这样添加。当然,如果您愿意,您可以自由地实现它。可能有些情况真的很有用,但它们是一个例外,而不是规范。

Python的哲学有什么好处?我们从这开始(在Python终端中探索它):

这些可能不是传统意义上的模式,但这些是以最优雅和最有用的方式定义“Pythonic”编程方法的规则。

我们还有PEP-8编码指南,有助于构建我们的代码。对我而言,当然有一些适当的例外。顺便说一句,PEP-8本身也鼓励这些例外:

但最重要的是:知道何时不一致 - 有时风格指南并不适用。如有疑问,请用最好的判断。看看其他的例子来决定什么看起来最好。不要犹豫,去问!

将PEP-8与Python的Zen(也是PEP - PEP-20)相结合,您将拥有创建可读和可维护代码的完美基础。添加设计模式,您已准备好创建具有一致性和可演变性的各种软件系统。

Python设计模式

什么是设计模式?

一切从四人帮(GOF)开始。如果您不熟悉GOF,请快速进行在线搜索。

设计模式是解决众所周知问题的常用方式。两个主要原则是基于GOF定义的设计模式:

面向接口编程,而不是面向实现编程

组合优于继承

从Python程序员的角度,我们来仔细看看这两个原则。

面向接口编程,而不是面向实现编程

想想Duck Typing。在Python中,我们不喜欢根据这些接口定义接口和程序类,是这样吗?但是,听我说!这并不意味着我们不会考虑接口,实际上我们一直在做这种Duck Typing。

让我们来谈谈一下臭名昭着的Duck Typing方法,看看它是如何适应这种范式的:面向接口编程。

如果它看起来像个鸭子并且像一个鸭子一样嘎嘎,那它就是一个鸭子!

我们不关心对象的本质,我们不必关心对象是什么; 我们只想知道是否能够做我们需要的(我们只对对象的接口感兴趣)。

对象可以嘎嘎叫吗?好吧,让它嘎嘎叫!

我们为鸭定义了一个接口吗?没有!我们是否面向接口编程而不是面向实现?是! 而且,我觉得这很好。

正如Alex Martelli在他关于Python中的设计模式的众所周知的演讲中指出的,“学会鸭子类型需要一段时间,但是以后可以节省您大量的工作!”

组合优于继承

现在这就是我所说的Pythonic原理!与在其他类中包装一个类(或更多的是几个类)相比,我创建了更少的类/子类。

不是这样做:

我们可以这样做:

优点很明显。我们可以限制包装类的方法是否暴露。我们可以在运行时注入持久化实例!例如,今天它是一个关系型数据库,但是明天可能是任何需要的接口(再次是那些讨厌的鸭子)。

组合对于Python来说是优雅而自然的。

行为模式

行为模式涉及对象之间的通信、对象如何交互以及如何完成给定的任务。根据GOF原则,Python中共有11种行为模式:职能链,命令,解释器,迭代器,中介者,备忘录,观察者,状态,策略,模板方法,访问者。

行为模式处理对象间通信,控制各种对象如何交互和执行不同的任务。

我发现这些模式非常有用,但这并不意味着其他模式作用不大。

Iterator(迭代器)

迭代器被构建进了Python,这是这个语言最强大的特性之一。多年前,我读到别人说是迭代器使Python变得非常优秀,到现在我仍然是这么认为的。如果您对Python迭代器和生成器有足够多的了解,那您就会知道关于这个独特Python模式需要知道的一切。

Chain of responsibility(职能链)

这个模式为我们提供了一种使用不同方式对待一个请求的方法,每种方法都针对请求的特定部分。您知道的,对于好的代码来说,最好的原则之一就是单一职责原则。

每一块代码必须做一件事,并且只能做一件事。

这一原则深深融入了这一设计模式。

例如,如果我们要过滤一些内容,我们可以实现不同的过滤器,每个过滤器都要做一件精确的事情,并且明确定义其过滤类型。这些过滤器可用于过滤令人反感的单词,广告和不适合的视频内容等。

Command(命令)

这是我作为程序员实现的Python设计模式之一。这提醒了我:模式不是被发明的,而是被发现的。他们存在,我们只需要找到并使用它们。很多年前为了实现我们的一个很大的项目我发现了这一点:一个特殊的所见即所得的XML编辑器。在代码中集中使用这个模式后,我在另外一些网站上看到了更多关于它的内容。

出于某些原因,我们需要从准备执行的内容开始,然后在需要时执行该命令模式。这样的好处是在这种方式下封装的动作可以使Python开发人员添加与执行操作相关的其他功能,例如撤消、重做或者保留操作历史等。

让我们看一下简单但经常使用的例子:

Creational Patterns(创造者模式)

首先需要指出的是创建者模式在Python中并不常用。为什么?因为Python语言本身的动态性质。

某位比我更有智慧的人曾经说过工厂方法是内置在Python中的。这意味着语言本身为我们提供了一种可以以足够优雅的方式创建对象的全部灵活性; 我们很少需要在这之上实现任何东西,如单例和工厂方法。

在一个Python设计模式教程中,我发现了有关创造者模式的描述,它是这样来声明的“模式提供了一种在隐藏创建逻辑的同时创建对象的方法,而不是使用new运算符直接实例化对象”。

它恰到好处地总结了这个问题:我们不需要在Python中使用new运算符!

不管怎样,让我们来看下如何实现其中的某一小部分,看下使用这样的模式我们能否从中获得好处。

Singleton(单例)

当我们要保证运行时只有一个给定类的实例存在时,可以使用Singleton模式。在Python中我们是否真的需要这个模式? 根据我的经验,简单创建一个实例并且随后使用它比实现单例模式更为容易。

但是如果您想实现它,这里有一个好消息:在Python中,我们可以改变实例化过程(以及其他任何东西)。记得我之前提过的__new__()方法吗?就是它了:

在这个示例中,Logger是一个单例。

在Python中使用单例模式有以下这些备选方案:

使用模块

在应用程序的顶层创建一个实例,可以是配置文件

将实例传递给需要它的每个对象。这是一个依赖注入,它是一个强大而容易掌握的机制。

Dependency Injection(依赖注入)

我不打算讨论依赖注入是否是一种设计模式,但我会说这是一个实现松耦合的非常好的机制,它有助于让我们的应用变的更加可维护和可扩展。把它和鸭子类型结合起来然后力量就总是会与您同在。

鸭子?人类?Python都不关心。它灵活得很!

我之所在这篇文章的创造者模式部分列出来是因为它处理了何时(或者更好是:何地)创建对象这个问题。它是在外部创造的。更准确的说,对象并不是在我们使用它的地方创建的,所以依赖关系也不会在消费它的地方创建。消费者代码接收这些外部的对象并使用它。更多参考,请阅读这个Stackoverflow问题最受欢迎的答案。

这是一个关于依赖注入很好的解释,而且它给了我们关于这个特殊技术一个很好的想法。基本上,答案解释了以下示例的问题:不要自己从冰箱里面拿东西来喝,而是声明一个需要。告诉您的父母您需要在午餐喝点东西。

Python为我们提供了全部需要的东西并且实现起来很简单。想一下它在Java和C#其他语言中可能的实现,您将很快意识到Python的美丽。

让我们来看一个简单的关于依赖注入的例子:

我们在Command这个类中注入了authenticator和authorizer方法。Command类所需要的是成功地执行他们,而无须担心他们实现的细节。这样的话,我们可以在Command类中使用任何我们在运行时决定使用的认证和授权机制。

我们已经展示了如何通过构造函数注入依赖关系,但是我们可以通过直接设置对象属性来轻松地注入它们,从而解锁更多的潜力:

关于依赖注入还有很多的东西要学;例如,好奇的人会去搜索IoC。

但是在您这样做之前,请阅读Stackoverflow上的另一个回答,对于这个问题投票最多的回答。

还有,我们只是演示了如何在Python中只使用内置的语言功能来实现这个美妙的设计模式。

让我们不要忘记这一切的意义:依赖注入技术允许非常灵活和容易的单元测试。想象一下,您可以随时更改数据存储的架构。模拟数据库将会变成一个微不足道的任务,不是吗?有关进一步的信息,您可以查看Toptal的Python模拟简介。

您可能也会想研究原型、生成器和工厂方法设计模式。

结构化模式

Facade(外观模式)

这可能是最为出名的Python设计模式。

假设您有一个系统有着大量的对象。每一个对象都提供了丰富的接口方法 。您可以使用这个系统做很多很多事情,但是如何简化这个接口呢?为什么不添加一个接口对象来暴露出所有API方法的精心设计的子集呢?用Facade(外观模式)!

Facade(外观模式)是一个优雅的Python设计模式。它是精简接口的一个完美方式。

Python外观模式示例:

没有什么惊喜,也没有技巧,Car类是一个外观模式,就是这样。

Adapter(适配器)

如果说外观模式用于精简接口,那么适配器就是关于修改接口。就像当系统期待鸭子时却只有牛一样。

假设您有一个将信息记录到给定目的地的工作方法。您的方法期望目标有一个write()方法(例如,每个文件对象都有)。

我会说这是一个写得很好的带依赖注入的方法,它拥有巨大的扩展能力。假设您想要纪录到某些UDP套接字中而不是某个文件里,您知道如何打开这个UDP套接字,但唯一的问题是套接字对象没有write()方法。您需要一个适配器!

但是为什么我发现适配器如此重要?好吧,当它有效地和依赖注入结合时,它就会给予我们极大的灵活性。当我们可以实现一个适配器并将新接口转换为众所周知的接口时,为什么要修改我们经过验证的代码来支持新的接口?

由于桥接和代理设计模式与适配器的相似性,您还应该了解和掌握它们。想一下在Python中实现他们是多么的简单,并且考虑一下在您的项目中可能通过哪些不同的方式来使用它们。

Decorator(装饰者模式)

哦,我们真是太幸运了!装饰者模式真的很好,而且我们也已经集成到了这门语言中。在Python中我最喜欢的是使用它来教我们使用最佳的实践。这并不是指我们不会意识到最佳实践(尤其和设计模式),但不管怎样使用Python 我感觉我正在遵循着最佳实践。就我个人而言,我发现Python的最佳实践是非常符合直觉的,这是新手和精英开发人员都赞赏的东西。

装饰者模式是关于引入额外的功能,特别的是它没有使用继承。

那么,让我们来看下如何不用内建的Python功能来装饰一个方法。以下是一个直截了当的示例。

这里不太好的是execute方法不仅仅是执行某些功能,还做了别的事情。我们没有遵循单一职责原则。

像下面这样简单编写会好点:

我们可以在另一个地方,在装饰者模式中实现任何授权和认证功能,如下所示:

现在的execute()方法是:

简单易读

只做一件事(至少在看代码时)

用身份验证装饰

装饰有授权

我们使用Python集成的装饰者语法编写类似的代码:

重要的是要注意,您没有限制像装饰者那样的活动。一个装饰者可能涵盖了整个类。唯一的要求是它们必须是可调用的。但对此我们毫无疑问;我们只需要定义__call__(self)方法。

您可能也想再进一步了解Python的函数工具(functools)模块。有待发现的东西还有很多!

结论

我已经展示了使用Python的设计模式是多么的自然和容易,而且我也展示了如何在Python中简单的编程。

“简单比复杂好,”记得吗?也许您已经注意到,没有一个设计模式是完全且正式描述的。没有展示复杂全面的实现。您需要“感觉”并以最适合您的风格和需要的方式实施它们。Python是一种伟大的语言,它给了您创作灵活、可重用代码的全部力量。

然而,它给您的远胜于这些。它给了您编写非常糟糕的代码的“自由”。不要这么做!不要重复(DRY),并且不要写超过80个字符的代码行。还有不要忘了在合适的地方使用设计模式;它是从别人那里学习并从他们丰富的免费经验中获益的最好方式之一。

来自:python部落

译者:Sarah

英文原文:https://www.toptal.com/python/python-design-patterns

Python开发整理发布,转载请联系作者获得授权

python的编程模式-Python设计模式:为了整洁又时尚的代码相关推荐

  1. python的编程模式-Python设计模式之状态模式原理与用法详解

    本文实例讲述了Python设计模式之状态模式原理与用法.分享给大家供大家参考,具体如下: 状态模式(State Pattern):当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类 ...

  2. python的编程模式-Python 编程,应该养成哪些好的习惯?

    我在模块还没认全,元类.类方法和静态方法的区别.装饰器写的还不溜等还不懂的时候,就开始注意让写的代码尽量Pythonic和符合PEP8标准. 到现在,基本手写的都是符合标准的Python代码.我写代码 ...

  3. python的编程模式-python编程(python开发的三种运行模式)【转】

    版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[-] 单循环模式 多线程模式 reactor模式 [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing ...

  4. python游戏编程入门 免费-python游戏编程入门 python游戏编程入门课

    python游戏编程入门 python游戏编程入门课 什么是python游戏编程入门?首先我们需要认识什么是Python Python既是一个软件工具包,也是一种语言.Python软件包包含了一个名为 ...

  5. python语言编程模式是啥-Python的设计模式编程入门指南

    有没有想过设计模式到底是什么?通过本文可以看到设计模式为什么这么重要,通过几个Python的示例展示为什么需要设计模式,以及如何使用. 设计模式是什么? 设计模式是经过总结.优化的,对我们经常会碰到的 ...

  6. python抽象工厂模式_Python设计模式之抽象工厂模式

    Python设计模式之抽象工厂模式 这篇文章主要为大家详细介绍了Python设计模式之抽象工厂模式,感兴趣的小伙伴们可以参考一下 python面向对象编程入门,我们需要不断学习进步 "&qu ...

  7. python基础编程语法-Python基础语法(Python基础知识点)

    Python与Perl,C和Java语言等有许多相似之处.不过,也有语言之间有一些明确的区别.本章的目的是让你迅速学习Python的语法. 第一个Python程序: 交互模式编程: 调用解释器不经过脚 ...

  8. python中文版编程软件-Python编程软件下载

    MRT7-Python软件是一款Python少儿编程软件,分为图块Boclky 编程模式.Python代码编程模式,同时也可以配合设备使用!软件使用都不是很困难,用户只需要根据自己的系统选择安装版本即 ...

  9. python语言编程入门-Python编程从入门到精通 PDF 下载

    相关截图: 资料简介: 本书循序渐进.由浅入深地详细讲解了Python语言开发技术,并通过具体实例演练了各个知识点的具体使用流程.全书共23章,其中第1-2章是基础知识部分,讲解了Python语言开发 ...

最新文章

  1. 一个表对应另一个表中多个主键的查询方法(把一个表当成两个表用)
  2. SSH整合框架+mysql简单的实现
  3. python课后题答案第五章_Python语言程序设计(美-梁勇)第5章习题解答
  4. ESP8266/ESP32自动下载电路分析
  5. ython二十五: 解压序列
  6. 笨方法学python之import sys与from sys import argv的区别
  7. 安卓逆向_4 --- Java 学习
  8. java实现权重随机算法
  9. 单点服务器微信公众号,腾讯云联合微信降低开发门槛 微信生态从单点云开发到全面云开发...
  10. 一次Linux服务器***查杀经历
  11. linux查看耗费流量的进程--iftop
  12. Python文件指针
  13. 逻辑回归模型(一)——数学模型
  14. 邻接矩阵的理解与应用
  15. 编程英文单字的标准缩写
  16. 国内外视频编解码标准体系-说说MPEG、ITU、AOM、AVS演进历程
  17. 包租婆再也不用为了收租、欠租而到处奔波了
  18. 磊科路由器信号按键_磊科无线路由器参数设置教程
  19. protobuf根据有关联的.proto文件进行编译
  20. 【Datawhale第25期组队学习】Task03:基于线性模型的异常检测

热门文章

  1. WebAssembly 浏览器中运行c/c++模块
  2. ASP.NET 163 smtp服务器响应为:User has no permission
  3. (第十一周)考试系统测试报告
  4. MAMP mac下启动Mysql
  5. ThinkPHP如何判断一个更新操作是否成功
  6. Objective-C语法之代码块(block)的使用
  7. TortoiseMerge tutorial
  8. SWFUpload上传文件组件,跨域上传文件
  9. 把时间当作朋友(四)
  10. mysql常见排错_MySQL常规日志排错