如何写一个更好的Python函数?
副标题[/!--empirenews.page--]
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)也清楚地表明了参数类型。 单一功能 单一功能原则不仅适用于类和模块,也同样适用于函数。 一个函数应该只有一个功能。也就是说,它应该只做一件事。 一个重要的原因是,如果每个函数只做一件事,只有这件事发生了变化,才需要改变这个函数。 此外,如果这个函数的单个功能不再需要了,直接把它删了就行了。 还是用例子来说明吧。下面这个函数,可以做不止一件“事情”: defcalculate_andprint_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。你可以测试一下这段代码: ? python3 Python3.7.0(default, Jul232018,20:22:55) [Clang9.1.0(clang-902.0.39.2)] on darwin Type"help","copyright","credits"or"license"formore information. >>>defadd(a, b): ... print(a + b) ... >>> b = add(1,2) 3 >>> b >>> bisNone True 你会发现 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)函数是幂等函数: 不管一个人调用add_three(7)多少次,答案总是10。以下是一个非幂等函数: defadd_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。 幂等性的一个现实中例子是在电梯前点击“向上”按钮。第一次按时,电梯会被“通知”你要上去。因为按按钮是幂等的,所以反复按它都没有什么影响。结果是一样的。 为什么幂等很重要? 可维护性和可维护性。幂等函数很容易测试,因为在使用相同的参数时,它们总是返回相同的结果。 测试仅仅是检查通过不同调用返回值的预期值。更重要的是,这些测试很快,这是单元测试中一个重要且经常被忽视的问题。 而在处理幂等函数时,重构是轻而易举的事情。 无论如何在函数之外更改代码,使用相同的参数调用它的结果总是一样的。 什么是纯函数? 在函数编程中,如果一个函数既幂等又没有可观察到的副作用,它就被认为是纯函数。函数外部的任何东西都不会影响这个值。 (编辑:上饶站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |