如果你是bash熟练技工,那你肯定知道bash命令行工具有多趁手: sed、 grep、 awk等;还有花式的重定向、管道,一条命令解决问题,简单明了心情舒畅。有没有办法让我们的脚本,也拥有这些原生命令行工具的使用体验呢?
当然有,entry point + Click,将python脚本打包成命令行工具的好帮手!本文能帮助你快速将你的python脚本包装成功能强大、调用方便的命令行工具,从此以后成为街上最靓的数据民工!
本文介绍的内容总结如下:
一套得心应手的工具有多重要,经常跟数据处理打交道的 数据民工 工程师可能深有体会。据说,数据处理工程的一天,是从不知道哪里来的祖传脚本、屎山数据开始,再以人模狗样的最终数据结束,中间留下来了一堆打满补丁的脚本和乱七八糟、随便命名的中间文件。别人以为我们生产的是数据,实际上大部分时间我们都在生产脚本。一个又一个的临时脚本,在代码复用上,鸡肋且为难。
而日常工作被乱七八糟、没有版本控制的脚本占据,不但非常影响工作体验,也非常直接地影响了你的 游戏时间 下班时间。如何在代码复用和满足千奇百怪需求中取得平衡呢?笔者在这个问题上也想了很久,下面给大家抛砖引玉一个。
很多时候,我们写了很多数据处理的脚本,杂乱无章地堆在各种各样的角落。这样做有什么坏处呢?
最大的坏处:程序如果很复杂很难搞,人就很容易烦躁;一烦躁,数据就做错了,又得全部重新来过;如果连做错了都不知道,下游算法出了问题,那就要被3.25,要背锅了。当数据工具人是最难的,正所谓做对了没功劳,做错了就背锅,没有功劳却也没有苦劳;咳咳,好像偏题了,回来回来。
所以,我们推荐使用一个脚本仓库,把自己的脚本给组织起来:
回到正题,今天讲的是python命令行接口。在具体介绍前,先给大家看看,利用entry point+Click,我们能达成什么效果。
有了多级命令,再多的功能,也能塞得下;再杂乱的功能,也能有逻辑性地组织起来。搞数据的姿势对了,原来可以这么快活^.^
那么,现在唯一的问题就是:要怎么搞?下面分为两个章节,给大家介绍实现方法。
entry_point是python在安装模块(module)时提供的一个功能,其能把模块中给定的函数包装成可执行文件,并部署到系统路径(即Linux中的 PATH路径)。具体路径视你的python环境不同而不同。举例子,如果你是anaconda环境,那么可执行文件就会安装在 anaconda3/env/$环境名字/bin
下。这里有个更全面的英文介绍。下面简要介绍一下流程:
首先,你要有个仓库。如果同学对python module不熟悉,那请先搜索引擎查一查学一学噢。
然后,编辑仓库根目录下的 setup.py文件。下面给一个python3例子:我们的module下面有一个代码文件夹和一个setup.py文件。
编辑setup文件,增加entry_points选项
解释一下,jsonl=my_tools.cli.command.jsonl:cli
指的是,在 my_tools/cli/command/jsonl.py
中有一个 cli()
函数,我们以其为接口,包装成一个命令行工具,名字为 jsonl
。
最后,记得执行安装模块操作 pip install -e .
,python才会真正把工具部署在可执行路径中:
Click是一个开源的python第三方库,专门用来生成命令行接口。其源代码在https://github.com/pallets/click ,文档是 文档。它是为了解决什么问题而诞生的呢?这里引用一下官方介绍:
Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It's the "Command Line Interface Creation Kit". It's highly configurable but comes with sensible defaults out of the box.
It aims to make the process of writing command line tools quick and fun while also preventing any frustration caused by the inability to implement an intended CLI API.
Click in three points:
* Arbitrary nesting of commands
* Automatic help page generation
* Supports lazy loading of subcommands at runtime
翻译一下:Click能让你用尽可能少的代码,以可组合的形式生成漂亮的命令行接口,有以下三个优点:
Click的官方文档很充足、易懂,这里就不对其进行二次介绍,希望同学能够自行阅读。这里有个其他好心人的中文学习笔记,英文捉急的同学可以参考下。
下面,仅给出,实现我们开头的例子所需要的三个关键魔法。
命令嵌套,能帮助我们把不同的脚本文件按照作用、类型、功能组织起来,达到更好的抽象性。有了嵌套,命令再多,也不担心乱掉。
这里给一个三级命令的命令嵌套例子。记住,命令的嵌套层数是任意的;但是,不要弄得过深,把握好度。
click提供了一个很方便的功能,能让你的工具让Linux原生工具一样,方便地使用各种管道功能。管道的使用,对工作效率的提升是肉眼可见的,举个例子:
当使用type为click.File
的click.argument
作为接口的参数时,就可以启用管道功能。其会自动根据输入打开输入流、或者输出流,以供程序使用。在bash中使用时,使用 - 代替 输入文件路径或者输出文件路径,便将输入输出重定向到管道中。
注意:当你像下面的程序一样使用 click.File时,得到的input、output,不再是一个文件路径,而是一个已经打开的文件对象。也就是说,就算你在bash中使用时用了真实的文件路径而不是 -,你的input也不会是一个string字符串,而相当于是一个open($input_string, “r”)
的文件对象。
下面是一个实现的例子:
有时候命令太长,很容易敲错;或者,单纯就是懒得敲,记不住,任性。这种情况下,我们就会想,要是有简略命令就好了。幸运的是,可以通过设置,让click支持简略命令。
通过以下代码,你就可以在二级命令、或者更高层的命令中使用命令的任意前缀,只要这个前缀不要短到引起歧义。
实现效果:
实现例子:
argparse是python内置的官方库,相信大家或多或少都用过。那么,Click相对于argparse有什么优劣呢?笔者从实用的角度总结了一下:
python run.py --inputs *.list --output result.txt
场景,与bash通配符(wildcards)结合起来非常方便;而Click无法支持。总的来说,argparse是不能不会的,但是Click是更好的那一个。两种姿势,同学们都得学习一番!
在前言里,我们给出了一种 数据民工 数据工程师的数据处理的脚本管理的可能技术路径。在正文里,我们先是展示了最终效果,然后介绍了entry_point的作用和实现方法,以及Click的作用和三个实用技巧,最后对比了一下Click和argparse的优劣。看完本文,你应该对python命令行接口的相关内容有一定的了解了,接下来请在实践中好好体会,理论联系实践。
最后请记住,结果是最重要的,但是磨刀不误砍柴工,我们的刀,要得心应手,才能随心所欲。