点击上方“磐创AI”,选择“置顶公众号”

精品文章,第一时间送达

本文经AI新媒体量子位(公众号 ID: QbitAI)授权转载,未经允许不得二次转载

Python虽然好用,但用好真的很难。

尤其是函数部分,只要写不好,后面的一连串人都会遭殃。

看又看不懂,测试起来也麻烦,维护又维护不动,真是让人头疼。

那怎么写好一个Python函数呢?

《Writing Idiomatic Python》一书的作者在Medium上发表了一篇文章,给出了6个建议。

希望能够给你带来帮助。

什么样的函数是一个好函数?

“好”的Python函数和“差”的Python函数之间有什么差别呢?每个人都有自己的理解。基于我的理解,如果一个Python函数能够符合下面的大部分条件,我会认为它是一个“好”函数:

命名合理

单一功能

包括文档字符串

返回一个值

不超过50行

是幂等函数或纯函数

对许多人来说,这些要求可能显得过于苛刻了。

不过,我保证,如果你的函数遵循这些规则,你的代码会非常漂亮,会让其他的程序员都“馋哭”的。

下面,我将一一讨论这些规则,然后总结它们是如何创造“好”函数的。

命名

在这个问题上,我最喜欢的一句话是:

计算机科学中只有两件事很让人头疼:缓存失效和命名。

尽管这听起来很莫名其妙,但给一个事情命名太难了。下面是一个反面案例:

defgetknn(from_df):

原文中这个代码没有放上去,量子位根据上下文信息进行了补充。

这个函数命名的第一个问题是它使用了缩写。

对于那些并不出名的缩略词来说,使用完整的英语单词会更好。缩写单词的唯一原因是为了节省打字时间,但是每个现代编辑器都有自动填充功能,所以你只需要键入一次全名就可以了。

缩写通常是特定领域的。在上面的代码中,KNN指的是“K-Nearest Neighbors”,df指的是“DataFrame”,这是一个数据结构。如果另一个不熟悉这些首字母缩写的程序员正在阅读代码,几乎很难看懂。

关于这个函数的名字还有另外两个小瑕疵:

“get”这个词是无关紧要的。对于大多数命名比较好的函数来说,很明显有一些东西会从函数中返回,它的名字将反映这一点。

from_df也不是必要的。如果没有明确的参数名称,函数的文档字符串或类型注释会描述参数的类型。

那么我们如何重命名这个函数呢?很简单:

defk_nearest_neighbors(dataframe):

即使是外行,这个函数要计算的内容也很清楚,参数的名称(dataframe)也清楚地表明了参数类型。

单一功能

单一功能原则不仅适用于类和模块,也同样适用于函数。

一个函数应该只有一个功能。也就是说,它应该只做一件事。

一个重要的原因是,如果每个函数只做一件事,只有这件事发生了变化,才需要改变这个函数。

此外,如果这个函数的单个功能不再需要了,直接把它删了就行了。

还是用例子来说明吧。下面这个函数,可以做不止一件“事情”:

def calculate_and print_stats(list_of_numbers):sum = sum(list_of_numbers)mean = statistics.mean(list_of_numbers)median = statistics.median(list_of_numbers)mode = statistics.mode(list_of_numbers)print(‘—————–Stats—————–‘)print(‘SUM: {}’.format(sum) print(‘MEAN: {}’.format(mean)print(‘MEDIAN: {}’.format(median)print(‘MODE: {}’.format(mode)

这个函数做了两件事:一是计算一组关于数字列表的统计数据,二是将它们打印到STDOUT。

如果需要计算新的或不同的统计数据,或者需要改变输出的格式,就需要对这个函数进行调整。

所以,这个函数最好写成两个独立的函数:一个用来执行并返回计算结果,另一个用来获取这些结果并打印出来。

这种处理方式,不仅能让测试函数更容易,并且还允许这两个部分有了迁移性,如果合适的话,还可能一起应用到不同的模块中。

在编程中,你会发现好多函数都可以做很多很多事情。同样,为了可读性和可测试性,这些函数应该被分解成更小的函数,每个函数只有一个功能。

文档字符串(Docstrings)

虽然每个人似乎都知道PEP – 8,它定义了Python的样式指南,但是很少有人知道PEP – 257,它是关于文档字符串的。我再这里不简单地重复PEP – 257的内容了,你可以在闲暇时读一下。其中的关键内容是:

每个函数都需要有一个文档字符串

使用适当的语法和标点符号;用完整的句子写

首先对函数的作用进行一句话的总结

使用说明性语言而不是描述性语言

在编写函数时,要养成写文档字符串的习惯,并在编写函数代码之前尝试写一下。

如果你不能写一个清晰的文档字符串来描述函数做什么,就说明你需要再考虑考虑为什么要写这个函数了。

返回值

函数可以被认为是一些独立的程序。它们以参数的形式接受一些输入,并返回一些结果。

参数有没有都可以,但从Python内部的角度来看,返回值是必须要有的。你不可能创建一个没有返回值的函数。如果函数没有返回值,Python会“强制”返回None。你可以测试一下这段代码:

❯ python3Python3.7.0(default, Jul232018,20:22:55)[Clang9.1.0(clang-902.0.39.2)] on darwinType“help”,“copyright”,“credits”or“license”formore information.>>>def add(a, b):…   print(a + b)…>>> b = add(1,2)3>>> b>>> bisNoneTrue

你会发现 b 的返回值实际上是 None。 即使你写的函数没有返回语句,它仍然会返回一些东西。而且,每个函数都应该返回一个有用的值,测试起来也会更方便。毕竟,你写的代码应该能够被测试。

试想一下,测试上面的add函会有多艰难。遵循这个概念,我们应该这样写代码:

withopen(‘foo.txt’,‘r’)asinput_file:forlineininput_file:ifline.strip().lower().endswith(‘cat’):# … do something useful with these lines

if line.strip().lower().endswith(‘cat’):这一行能够工作,是因为每个字符串方法( strip ( )、lower ( )、end swith ( ) )都返回一个字符串作为调用函数的结果。

当给定函数没有返回值时,有一些常见的原因:

“它所做的只是[一些与I / O相关的事情,比如将一个值保存到数据库中]。我不能返回任何有用的东西。”

我不同意。如果操作顺利完成,函数可以返回True。

“我们修改了其中一个参数,将其用作参考参数。”

这里有两点需要注意。首先,尽最大努力避免这种做法。用好了令人惊讶,用不好非常危险。

其次,即使这样做不可行,复制某个参数的成本太高,你也可以回到上一条建议。

“我需要返回多个值。单独返回一个值是没有意义的。”

可以使用元组返回多个值。

总是返回一个有用的值,调用者总是可以自由地忽略它们。

函数长度

让你读一个200行的函数,并说出它是做什么的,你是什么感受?

函数的长度直接影响可读性,从而影响可维护性。所以要保持你的函数简短。50行是一个随意的数字,在我看来是合理的。你编写的大多数函数应该要短一些。

如果一个函数遵循单一功能原则,它很可能是相当短的。 如果它是纯函数或是幂等的(下面讨论) ,它也可能是短的。

那么,如果函数太长,应该怎么做?重构。这会改变程序的结构而不改变其行为。

从一个长函数中提取几行代码,并把它们变成自己的函数。这是缩短长函数的最快、也是最常见的方式。

加上你给所有这些新函数取了合适的名称,因此生成的代码读起来也会更容易。

幂等和函数纯度

不管被调用了多少次,幂等函数总是在给定相同参数集的情况下返回相同的值。

结果不依赖于非局部变量、参数的可变性或来自任何I / O流的数据。下面的这个add_three(number)函数是幂等函数:

def add_three(number):    """Return *number* + 3."""    return number + 3

不管一个人调用add_three(7)多少次,答案总是10。以下是一个非幂等函数:

def add_three():“””Return 3 + the number entered by the user.”””number = int(input(‘Enter a number: ‘))returnnumber +3

这个函数的返回值取决于I / O,即用户输入的数字。对add_three()的每次调用都会返回不同的值。

如果它被调用两次,用户可以第一次输入3,第二次输入7,分别调用add_three()返回6和10。

幂等性的一个现实中例子是在电梯前点击“向上”按钮。第一次按时,电梯会被“通知”你要上去。因为按按钮是幂等的,所以反复按它都没有什么影响。结果是一样的。

为什么幂等很重要?

可维护性和可维护性。幂等函数很容易测试,因为在使用相同的参数时,它们总是返回相同的结果。

测试仅仅是检查通过不同调用返回值的预期值。更重要的是,这些测试很快,这是单元测试中一个重要且经常被忽视的问题。

而在处理幂等函数时,重构是轻而易举的事情。 无论如何在函数之外更改代码,使用相同的参数调用它的结果总是一样的。

什么是纯函数?

在函数编程中,如果一个函数既幂等又没有可观察到的副作用,它就被认为是纯函数。函数外部的任何东西都不会影响这个值。

然而,这并不意味着函数不能影响非局部变量或I / O流之类的事情。例如,如果上面add_three(number)的幂等版本在返回结果之前打印了结果,那么它仍然被认为是幂等的,因为当它访问I / O流时,这个访问与从函数返回的值无关。

调用print ( )只是一个副作用:除了返回值之外,还与程序的其他部分或系统本身进行了一些交互。

让我们把我们的add_three(number)示例再向前推进一步。我们可以编写下面的代码片段来确定调用add_three(number)的次数:

add_three_calls =0def add_three(number):“””Return *number* + 3.”””globaladd_three_callsprint(f’Returning {number + 3}‘)add_three_calls +=1returnnumber +3def num_calls():“””Return the number of times *add_three* was called.”””returnadd_three_calls

我们现在正在打印到控制台(一个副作用)并修改一个非局部变量(另一个副作用),但是由于这两者都不影响函数返回的值,它仍然是幂等的。

纯函数没有副作用。它不仅不使用任何“外部数据”来计算值,除了计算和返回所述值之外,它与系统/程序的其余部分都没有交互。因此,虽然我们新的add_three(number)定义仍然是幂等的,但它不再是纯的。

纯函数没有日志语句或print ( )调用。它们不使用数据库或互联网连接。它们不访问或修改非局部变量。它们不调用任何其他非纯函数。

简而言之,它们无法做到爱因斯坦所说的“远距离幽灵般的行动”(在计算机科学环境中)。它们不会以任何方式修改程序或系统的其余部分。

在命令式编程(编写Python代码时所做的那种)中,它们是所有函数中最安全的函数。

它们也很容易被测试和维护,甚至比只是幂等函数更重要的是,测试它们基本上可以和执行它们一样快。

测试本身很简单:没有数据库连接或其他外部资源进行模拟,也不需要安装代码,之后也没有什么需要清理的。

明确地说,幂等性和纯函数只是一种期望,不是必需的。也就是说,由于好处很多,我们可能会希望只编写纯函数或幂等函数,但这不现实。

重要的是,我们要有意识开始写代码来隔离副作用和外部依赖性。这会使得我们编写的每一行代码都更容易被测试。

原文链接:

https://hackernoon.com/write-better-python-functions-c3a9a36382a6

你也许还想看:

欢迎扫码关注:

点击下方|阅读原文|了解更多

python怎么写ai_干货 | 如何写一个更好的Python函数?相关推荐

  1. 干货 | 如何写一个更好的Python函数?

    乾明 编译整理自 Medium  量子位 报道 | 公众号 QbitAI Python虽然好用,但用好真的很难. 尤其是函数部分,只要写不好,后面的一连串人都会遭殃. 看又看不懂,测试起来也麻烦,维护 ...

  2. python 公众号文章发布_分享一个牛逼的Python项目:公众号文章爬虫

    我订阅了近 100 个公众号,有时候想再找之前读过的文章,发现搜索起来特别困难,如果忘了收藏,估计得找半小时,更让人无语的是,文章已经发布者删除,或者文章因违规被删除.那么有没有这样的爬虫,可以将公众 ...

  3. python手机版怎么用-QPython,一个在手机上运行Python的神器

    之前安利过一款手机上运行Python的神器Termux,不过Termux的使用比较重,它实际是一款linux系统模拟器,安装好Termux后还要再安装python,并且是全命令行操作,一些读者使用起来 ...

  4. python编程案例教程-quot;怎样编写一个教学案例quot;python编程案例教程电子教案...

    怎样编写一个教学案例 1.什么是教学案例 (案是论) 教育教学案例是含有解决某些疑难问题,某些原方法.策略运用的教育教学情境故事的描述,故事中渗透课程改革的思想和理念,展现在教育教学理论.方法指导下解 ...

  5. python的项目骨架_练习 46 - 一个项目骨架 - Learn Python 3 The Hard Way

    练习 46. 一个项目骨架 这个练习你将学习如何创建一个好的项目"骨架"(skeleton)目录.这个骨架目录具备让项目跑起来的所有基本内容.它里边会包含你的项目文件布局.自动化测 ...

  6. python前n项和存为一个数组_在Python中存储多个数组

    我正在编写一个程序来模拟像Gallup或Rasmussen这样的公司每天发布的实际投票数据:www.gallup.com和www.rassmussenreports.com 我正在使用强力方法,计算机 ...

  7. python语言编程流程图-用流程图带你更好理解python语法

    前言 本系列课程是针对无基础的,争取用简单明了的语言来讲解,学习前需要具备基本的电脑操作能力,准备一个已安装python环境的电脑.如果觉得好可以分享转发,有问题的地方也欢迎指出,在此先行谢过. 这里 ...

  8. python图表可视化工具_比Excel制图更强大,Python可视化工具Altair入门教程

    原作者 Parul Pandey 晓查 编译整理 量子位 出品 | 公众号 QbitAI 数据转化成更直观的图片,对于理解数据背后的真相很有帮助.如果你有这方面的需求,而且还在使用Python,那么强 ...

  9. python发音1001python发音-怎样才能写出 Pythonic 的代码 #P1001#

    L = [ i*i fori inrange(5) ] forindex, data inenumerate(L, 1):print(index, ':', data) 去除 import 语句和列表 ...

  10. 初学者python编辑器-分享|Mu 入门:一个面向初学者的 Python 编辑器

    相识 Mu -- 一个可以使学生学习 Python 更轻松的开源编辑器. Mu 是一个给初学者的 Python 编辑器,它旨在使学习体验更加愉快.它使学生​​能够在早期体验成功,这在你学习任何新知识的 ...

最新文章

  1. python开源库推荐_推荐5个值得关注的Python开源项目
  2. FCOS:全卷积一阶段Anchor Free物体检测器,多种视觉任务的统一框架
  3. 如何用计算机寒假计划表,寒假学习计划表
  4. 安装了email模块还是报错_科普:利用Python smtplib和email模块实现自动发送邮件功能...
  5. Java Review - 并发编程_原子操作类原理剖析
  6. Scala 字符串详解
  7. 爬虫-性能相关- twisted-tornado
  8. Python接口自动化测试系列文章汇总
  9. [恢]hdu 2000
  10. 安卓实现百度地图定位
  11. Java泛型编程思想
  12. ES6 模板字符串基本用法
  13. Windows10更新安装失败,错误0x80070005
  14. 有两个瓶子,一个瓶子是5升_隐藏的功能,装在瓶子中的消息和痴呆的面包师。...
  15. AC/DC 电源适配器拆解
  16. 关于聪明与努力的文章.
  17. 为了中国---我国民用客机深度报道(第三部分下)
  18. python从文件中读取数据_【Python】从文件中读取数据
  19. geo读取表达矩阵 RNA-seq R语言部分(表达矩阵合并及id转换)
  20. java后端和web_从java和web角度分析前端好还是后端好

热门文章

  1. java和XML转换(一)
  2. 您在运行该虚拟机时启用了侧通道缓解。侧通道缓解可增强安全性,但也会降低性能
  3. 从300元到300万 大四女生创造财富神话
  4. 日常维护压力传感器有哪些方法?-道合顺大数据Infinigo
  5. 开源!我知道你不知道,百度开源词法LAC 2.0帮你更懂中文
  6. IDEA文件修改不生效
  7. 正则表达式中中括号的三种用途 []
  8. windows安装生成苹果证书
  9. web笔记javaScript-02
  10. 国内名校两万名留学生拒绝回国,国家终于出手了!