Python开发者面向文档编程的正确姿势

简介:

Python开发者面向文档编程的正确姿势


概述

秦人不暇自哀,而后人哀之;后人哀之而不鉴之,亦使后人而复哀后人也! –论面向文档编程的重要性

如果想看见识一个人写代码的功力,注释其实是区分老司机和小鲜肉的一个显著的分界线(有没有观察到你们公司的领导基本都在开会或者写文档),通常情况下老司机的文档量与代码量是1:1的比例,而新人往往认为写完功能模块就已经可以完成任务了。生产环境中需要面对现实中大量复杂的业务逻辑和数据校验并与各方对接,文档质量和代码质量就被提升到了相同的高度。很多人没有写注释的习惯,大多数不是因为懒惰,一方面是没有意识到写文档的好处,另一方面是不了解这方面的工具。毕竟从管理上依赖于人的主动性是远不如依赖于工具有效的。本文介绍如何利用Python注释提升文档书写的质量以及效率的小技巧。

Python

在实际生产中,机器学习工作现在看起来,白天像是个算法工程师的活,晚上就变成运维+测试了。Python 一直以来也都受到测试工程师和运维工程师的偏爱,下面是几个经典的注释活用case。

用注释写单元测试:doctest

单元测试是代码开发环节必不可少的一环,对于Bug定位和代码质量而言是非常重要的。现在最广为人知的单元测试框架就是Unittest,它借鉴了Java中成熟的单元测试框架的JUnit。即使像Django还对这个框架有特殊的支持,然而在实现Unittest的时候会感觉确实比较啰嗦,setup,teardown…在维护单元测试的时候很多时候感觉力不从心。

一个巧妙的方式可以是通过doctest,用docstring注释的方式来完成单元测试,由于每个方法def下面都先跟着一段测试用例,然后紧跟着就是代码正文,这样一来很方便我们测试现有代码的质量,另一方面又便于修改。

举个例子:


 
 
  1. def factorial(n): 
  2.  
  3.     """Return the factorial of n, an exact integer >= 0. 
  4.  
  5.   
  6.  
  7.     >>> [factorial(n) for n in range(6)] 
  8.  
  9.     [1, 1, 2, 6, 24, 120] 
  10.  
  11.     >>> factorial(30) 
  12.  
  13.     265252859812191058636308480000000 
  14.  
  15.     >>> factorial(-1) 
  16.  
  17.     Traceback (most recent call last): 
  18.  
  19.         ... 
  20.  
  21.     ValueError: n must be >= 0 
  22.  
  23.   
  24.  
  25.     Factorials of floats are OK, but the float must be an exact integer
  26.  
  27.     >>> factorial(30.1) 
  28.  
  29.     Traceback (most recent call last): 
  30.  
  31.         ... 
  32.  
  33.     ValueError: n must be exact integer 
  34.  
  35.     >>> factorial(30.0) 
  36.  
  37.     265252859812191058636308480000000 
  38.  
  39.   
  40.  
  41.     It must also not be ridiculously large: 
  42.  
  43.     >>> factorial(1e100) 
  44.  
  45.     Traceback (most recent call last): 
  46.  
  47.         ... 
  48.  
  49.     OverflowError: n too large 
  50.  
  51.     ""
  52.  
  53.   
  54.  
  55.     import math 
  56.  
  57.     if not n >= 0: 
  58.  
  59.         raise ValueError("n must be >= 0"
  60.  
  61.     if math.floor(n) != n: 
  62.  
  63.         raise ValueError("n must be exact integer"
  64.  
  65.     if n+1 == n:  # catch a value like 1e300 
  66.  
  67.         raise OverflowError("n too large"
  68.  
  69.     result = 1 
  70.  
  71.     factor = 2 
  72.  
  73.     while factor <= n: 
  74.  
  75.         result *= factor 
  76.  
  77.         factor += 1 
  78.  
  79.     return result 
  80.  
  81.   
  82.  
  83.   
  84.  
  85. if __name__ == "__main__"
  86.  
  87.     import doctest 
  88.  
  89.     doctest.testmod()  

上面是官网提供的一个求N的阶乘函数示例,在docstring 中通过 >>>符号来开始一个单元测试,之后换行输入预期结果即可。实际上就是复制粘贴一下调试过程和结果,真的再简单不过了,想实现TDD也因此变得非常轻松。

用注释写API文档:apidoc

在我们完成机器学习模型后,想要提供一个对外服务的接口以贡献我们的算力时就需要完备的API文档,也是通过API的调用才能为我们的模型提供源源不断的校验数据,对于提升模型效果有非常实际的意义。对大多数人而言调用API来完成开发都是一件比较开心的事情,因为我们可以少做很多工作就可以实现强大功能。然而,当我们需要对外提供API时就要面临不一样的考验了,接口鉴权、接口设计、版本控制、并发问题、日志埋点…这些都是需要面对的新问题,而利用 apidoc 可以很好地解决这些API文档中常见的诸多问题,相当于通过模板提升了我们的接口设计的能力。

apidoc为Python提供了一种类似于 docstring 的方式来写API文档,从语法上看比较类似于 R中的roxygen,都需要用户以 @xxx 符号作为一个开头,随后书写相关的定义和功能。

举个例子:

下面是一个API接口的定义方法,最核心的部分就是

  1. 路由
  2. GET/POST方法
  3. 名称/分组
  4. 参数与调用例子

(这年头没有用例的代码都是耍流氓)


 
 
  1. ""
  2.  
  3. @api {get} /user/:id Request User information 
  4.  
  5. @apiName GetUser 
  6.  
  7. @apiGroup User    
  8.  
  9. @apiParam {Number} id Users unique ID.   
  10.  
  11. @apiSuccess {String} firstname Firstname of the User
  12.  
  13. @apiSuccess {String} lastname  Lastname of the User
  14.  
  15. """  

我们可以直接撸一个官方示例来学习如何使用apidoc。

首先,下载示例源码


 
 
  1. git clone https://github.com/apidoc/apidoc 
  2.  
  3. cd apidoc  

然后,安装 apidoc 组件


 
 
  1. sudo npm install apidoc -g 

接着,利用官方代码来制作一个例子,并且访问即可。


 
 
  1. apidoc -i example/ -o output/ -t template/ 
  2.  
  3. open output/index.html  

几个参数的含义如下:

-i:input,表示输入的文件夹

-o:output,表示输出文件夹

-t:template,表示模板文件,通过替换模板我们可以修改文档皮肤

在 example 文件夹下,我们需要在apidoc.json 中填写配置文件,定义文档的header和footer部分内容,其余的文件会被自动识别出其中的docstring作为API文档的一部分。

由于apidoc的官方文档非常简单清晰,所以这里不过多强调语法。

apidoc 还为我们提供了接口调试的功能,在实际使用的时候要注意:

我们需要一个web server 才可以使用这个接口调试的功能

要注意跨域的问题。

通过版本对比,我们还可以快速排查API接口的变化情况。需要注意的是这个功能要求我们要将历史的文档记录也要保存在该目录下的文件中,通常我们可以把历史的注释输出到一个特定文件中保存。

总的来说,虽然,API文档的书写并不是一件难度非常高的事情,却能体现系统模块设计和用户体验设计的功力,我们应该对那些无代码示例,无版本控制的API文档say no!

用注释写命令行接口:docopt

利用docopt,我们可以在注释中直接声明文件的命令行传入参数,而不需要通过 argvs变量来捕获输入值再做判断,这在调用运维脚本或者若干任务调度脚本的时候尤其管用,极大地提升了CLI的效率。

举个例子:(此处代码仅供参考)


 
 
  1. """Usage: 
  2.  
  3.   fiannceR.py tcp <host> <port> [--timeout=<seconds>] 
  4.  
  5.   fiannceR.py serial <port> [--baud=9600] [--timeout=<seconds>] 
  6.  
  7.   fiannceR.py -h | --help | --version 
  8.  
  9.   
  10.  
  11. ""
  12.  
  13. from docopt import docopt 
  14.  
  15.   
  16.  
  17. if __name__ == '__main__'
  18.  
  19.     arguments = docopt(__doc__, version='0.1.1rc'
  20.  
  21.     print(arguments)  

 
 
  1. fiannceR.py tcp 0.0.0.0 3838 

这里的 arguments 将传出一个字典对象,以Key-Value的形式将命令行中的输入值捕获。


 
 
  1. {'--baud': None, 
  2.  
  3. '--help'False
  4.  
  5. '--timeout': None, 
  6.  
  7. '--version'False
  8.  
  9. '-h'False
  10.  
  11. '<host>''0.0.0.0'
  12.  
  13. '<port>''3838'
  14.  
  15. 'serial'False
  16.  
  17. 'tcp'True}  

总结

如果真的要从数据撸到模型、接口,那么一排注释的画面真是美得不敢想象。


 
 
  1. """unitest 
  2.  
  3. >>> FinanceR('20161001'
  4.  
  5. 21.01 
  6.  
  7. ""
  8.  
  9. def FinanceR(date): 
  10.  
  11.     price = get_price(date
  12.  
  13.     return(price) 
  14.  
  15.   
  16.  
  17. class(BaseHandler): 
  18.  
  19.     def get(self):    
  20.  
  21.         """apidoc 
  22.  
  23.            @api {get} /price/:date 获取当前价格 
  24.  
  25.            @apiName GetPrice 
  26.  
  27.            @apiGroup Quota 
  28.  
  29.   
  30.  
  31.            @apiParam {Number} date 交易日期 
  32.  
  33.   
  34.  
  35.            @apiSuccess {String} price 
  36.  
  37.         ""
  38.  
  39.         date = self.get_argument('date',None) 
  40.  
  41.         try: 
  42.  
  43.             price = FinanceR(date
  44.  
  45.             self.write({'data':{'price':price},'response':{'message':'success','code':200}}) 
  46.  
  47.         except Exception as e: 
  48.  
  49.             self.write({'data':None,'response':{'message':str(e),'code':404}}) 
  50.  
  51.              
  52.  
  53. """Usage: 
  54.  
  55.   fiannceR.py tcp <host> <port> [--timeout=<seconds>] 
  56.  
  57.   fiannceR.py serial <port> [--baud=9600] [--timeout=<seconds>] 
  58.  
  59.   fiannceR.py -h | --help | --version 
  60.  
  61.   
  62.  
  63. ""
  64.  
  65. from docopt import docopt 
  66.  
  67.   
  68.  
  69. if __name__ == '__main__'
  70.  
  71.     arguments = docopt(__doc__, version='0.1.1rc'
  72.  
  73.     print(arguments)  

欢迎大家留言讨论,给出更多应用案例,交流分享。


作者:HarryZhu

来源:51CTO

相关文章
|
7天前
|
存储 数据挖掘 开发者
Python编程入门:从零到英雄
在这篇文章中,我们将一起踏上Python编程的奇幻之旅。无论你是编程新手,还是希望拓展技能的开发者,本教程都将为你提供一条清晰的道路,引导你从基础语法走向实际应用。通过精心设计的代码示例和练习,你将学会如何用Python解决实际问题,并准备好迎接更复杂的编程挑战。让我们一起探索这个强大的语言,开启你的编程生涯吧!
|
13天前
|
机器学习/深度学习 人工智能 TensorFlow
人工智能浪潮下的自我修养:从Python编程入门到深度学习实践
【10月更文挑战第39天】本文旨在为初学者提供一条清晰的道路,从Python基础语法的掌握到深度学习领域的探索。我们将通过简明扼要的语言和实际代码示例,引导读者逐步构建起对人工智能技术的理解和应用能力。文章不仅涵盖Python编程的基础,还将深入探讨深度学习的核心概念、工具和实战技巧,帮助读者在AI的浪潮中找到自己的位置。
|
13天前
|
机器学习/深度学习 数据挖掘 Python
Python编程入门——从零开始构建你的第一个程序
【10月更文挑战第39天】本文将带你走进Python的世界,通过简单易懂的语言和实际的代码示例,让你快速掌握Python的基础语法。无论你是编程新手还是想学习新语言的老手,这篇文章都能为你提供有价值的信息。我们将从变量、数据类型、控制结构等基本概念入手,逐步过渡到函数、模块等高级特性,最后通过一个综合示例来巩固所学知识。让我们一起开启Python编程之旅吧!
|
13天前
|
存储 Python
Python编程入门:打造你的第一个程序
【10月更文挑战第39天】在数字时代的浪潮中,掌握编程技能如同掌握了一门新时代的语言。本文将引导你步入Python编程的奇妙世界,从零基础出发,一步步构建你的第一个程序。我们将探索编程的基本概念,通过简单示例理解变量、数据类型和控制结构,最终实现一个简单的猜数字游戏。这不仅是一段代码的旅程,更是逻辑思维和问题解决能力的锻炼之旅。准备好了吗?让我们开始吧!
|
4天前
|
数据采集 存储 数据处理
Python中的多线程编程及其在数据处理中的应用
本文深入探讨了Python中多线程编程的概念、原理和实现方法,并详细介绍了其在数据处理领域的应用。通过对比单线程与多线程的性能差异,展示了多线程编程在提升程序运行效率方面的显著优势。文章还提供了实际案例,帮助读者更好地理解和掌握多线程编程技术。
|
7天前
|
存储 人工智能 数据挖掘
Python编程入门:打造你的第一个程序
本文旨在为初学者提供Python编程的初步指导,通过介绍Python语言的基础概念、开发环境的搭建以及一个简单的代码示例,帮助读者快速入门。文章将引导你理解编程思维,学会如何编写、运行和调试Python代码,从而开启编程之旅。
29 2
|
8天前
|
存储 Python
Python编程入门:理解基础语法与编写简单程序
本文旨在为初学者提供一个关于如何开始使用Python编程语言的指南。我们将从安装Python环境开始,逐步介绍变量、数据类型、控制结构、函数和模块等基本概念。通过实例演示和练习,读者将学会如何编写简单的Python程序,并了解如何解决常见的编程问题。文章最后将提供一些资源,以供进一步学习和实践。
18 1
|
10天前
|
存储 网络协议 IDE
从零起步学习Python编程
从零起步学习Python编程
|
14天前
|
机器学习/深度学习 存储 算法
探索Python编程:从基础到高级应用
【10月更文挑战第38天】本文旨在引导读者从Python的基础知识出发,逐渐深入到高级编程概念。通过简明的语言和实际代码示例,我们将一起探索这门语言的魅力和潜力,理解它如何帮助解决现实问题,并启发我们思考编程在现代社会中的作用和意义。
|
9天前
|
机器学习/深度学习 存储 数据挖掘
Python 编程入门:理解变量、数据类型和基本运算
【10月更文挑战第43天】在编程的海洋中,Python是一艘易于驾驭的小船。本文将带你启航,探索Python编程的基础:变量的声明与使用、丰富的数据类型以及如何通过基本运算符来操作它们。我们将从浅显易懂的例子出发,逐步深入到代码示例,确保即使是零基础的读者也能跟上步伐。准备好了吗?让我们开始吧!
19 0
下一篇
无影云桌面