巧用Google Fire简化Python命令行程序

2023-11-28 02:08

本文主要是介绍巧用Google Fire简化Python命令行程序,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Hello World

要介绍Fire是什么,看一个简单的例子就明白了

# calc.py
import fireclass Calculator(object):"""A simple calculator class."""def double(self, number):return 2 * numberif __name__ == '__main__':fire.Fire(Calculator)

接下来我们进入bash来执行上面编写的脚本

> python calc.py double 10
20
> python calc.py double --number=16
32

上面是官方的示例代码,有了fire,编写Python的命令行程序就变得非常简单,我们无需再去处理繁琐的命令行参数解析了。接下来我们仿照HelloWorld,编写一个圆周率和阶乘计算的命令行脚本。

实战

import math
import fireclass Math(object):def pi(self, n):s = 0.0for i in range(n):s += 1.0/(i+1)/(i+1)return math.sqrt(6*s)def fact(self, n):s = 1for i in range(n):s *= (i+1)return sif __name__ == '__main__':fire.Fire(Math)

接下来我们运行一下

>  python maths.py pi 10000
3.14149716395
>  python maths.py pi 100000
3.14158310433
>  python maths.py pi 1000000
3.14159169866
>  python maths.py fact 10
3628800
>  python maths.py fact 15
1307674368000
>  python maths.py fact 20
2432902008176640000

Cool,真的非常方便!fire对当前对象结构进行了暴露,将结构信息映射到shell命令行参数上。fire其实有多种暴露模式,接下来我们逐个来看fire都有哪些暴露模式。

暴露模块

fire如果不传递任何参数就可以直接暴露当前模块结构,我们对上面的例子做一下改造,去掉类信息

import math
import firedef pi(n):s = 0.0for i in range(n):s += 1.0/(i+1)/(i+1)return math.sqrt(6*s)def fact(n):s = 1for i in range(n):s *= (i+1)return sif __name__ == '__main__':fire.Fire()

注意Fire函数调用没有任何参数,运行一下

>  python maths.py fact 20
2432902008176640000
>  python maths.py pi 1000000
3.14159169866

暴露函数

fire还可以传递一个函数对象来暴露单个函数,可以让我们在命令行参数上省掉函数名称

import math
import firedef pi(n):s = 0.0for i in range(n):s += 1.0/(i+1)/(i+1)return math.sqrt(6*s)if __name__ == '__main__':fire.Fire(pi)

如果暴露函数那就只能暴露一个函数,如果暴露了两个,那就只有后面一个生效,运行一下

>  python maths.py 1000
3.14063805621

暴露字典

fire可以直接暴露一个模块,将当前模块的所有函数全部暴露,函数名和第一个参数名一致。我们也可以不用暴露整个模块的所有函数,使用字典暴露法就可以选择性地对模块的某些函数进行暴露,顺便还可以替换暴露出来的函数名称。

import math
import firedef pi(n):s = 0.0for i in range(n):s += 1.0/(i+1)/(i+1)return math.sqrt(6*s)def fact(n):s = 1for i in range(n):s *= (i+1)return sif __name__ == '__main__':fire.Fire({"pi[n]": pi})

我们只暴露了pi函数,并且把名字还换掉了,运行一下,看效果

>  python maths.py pi[n] 1000
3.14063805621

如果我们使用原函数名称,就会看到fire列出的友好的报错信息

>  python maths.py pi 1000
Fire trace:
1. Initial component
2. ('Cannot find target in dict:', 'pi', {'pi[n]': <function pi at 0x10a062c08>})
Type:        dict
String form: {'pi[n]': <function pi at 0x10a062c08>}
Length:      1
Usage:       maths.pymaths.py pi[n]

暴露对象

import math
import fireclass Maths(object):def pi(self, n):s = 0.0for i in range(n):s += 1.0/(i+1)/(i+1)return math.sqrt(6*s)def fact(self, n):s = 1for i in range(n):s *= (i+1)return sif __name__ == '__main__':fire.Fire(Maths())

运行

>  python maths.py pi 1000
3.14063805621
>  python maths.py fact 20
2432902008176640000

暴露类

这个我们在上面的实战环节已经演示过了,这里就不在重复粘贴

类 vs 对象

通过上面的例子,我们发现暴露类和暴露对象似乎没有任何区别,那到底该选哪种比较优雅呢?这个要看类的构造器有没有参数,如果是不带参数的构造器,那么类和对象的暴露是没有区别的,但是如果类的构造器有参数,那就不一样了,下面我们改造一下Maths类,增加一个放大系数。

import math
import fireclass Maths(object):def __init__(self, coeff):self.coeff = coeffdef pi(self, n):s = 0.0for i in range(n):s += 1.0/(i+1)/(i+1)return self.coeff * math.sqrt(6*s)def fact(self, n):s = 1for i in range(n):s *= (i+1)return self.coeff * sif __name__ == '__main__':fire.Fire(Maths)

因为Maths的构造器带有参数,所有运行命令行时需要指定构造器参数值

> python maths.py pi 1000 --coeff=2
6.28127611241

如果不指定参数的值,运行时就会报错

> python maths.py pi 1000
Fire trace:
1. Initial component
2. ('The function received no value for the required argument:', 'coeff')
Type:        type
String form: <class '__main__.Maths'>
File:        ~/source/rollado/maths.py
Line:        5
Usage:       maths.py COEFFmaths.py --coeff COEFF

如果改成暴露对象,那么放大系数就是在代码里写死的,无法在命令行进行参数定制了。这就是暴露对象和暴露类的差别,似乎暴露类在功能上更强大一些。

暴露属性

上面的所有例子我们最终暴露的都是函数,要么是模块里的函数,要么是类里的函数。但实际上fire还可以暴露属性,比如我们可以将上面的coeff参数通过命令行进行输出。

> python maths.py coeff --coeff=2
2
> python maths.py coeff --coeff=3
3

再来一个更加简单的例子

# example.py
import fire
english = 'Hello World'
spanish = 'Hola Mundo'
fire.Fire()

运行

$ python example.py english
Hello World
$ python example.py spanish
Hola Mundo

原理

640?wx_fmt=png

命令行中的参数顺序和代码内部对象的树状层次结构呈现一一对应关系。如果fire不带参数暴露了当前的模块,那么第一个参数就应该是这个模块内部的函数名、类名或者是变量名。如果第一个参数是函数,那么接下来的参数就是函数的参数。如果第一个参数是类,那么接下来的参数可能是这个类实例内部的方法或者字段。如果第一个参数是变量名,后面没有参数的话,就直接显示这个变量。如果后面还有参数,那么就把这个变量看成一个对象,然后继续使用后续参数来深入解析这个对象。

在Python里面所有的变量都是对象,包括普通的整数、字符串、浮点数、布尔值等。理论上可以一直将对象结构递归下去,形成一个复杂的链式调用。

链式暴露

接下来我们验证这个理论,尝试一下复杂的链式暴露。

import fireclass Chain(object):def __init__(self):self.value = 1def incr(self):print "incr", self.valueself.value += 1return selfdef decr(self):print "decr", self.valueself.value -= 1return selfdef get(self):return self.valueif __name__ == '__main__':fire.Fire(Chain)

运行一下

> python chains.py incr incr incr decr decr get
incr 1
incr 2
incr 3
decr 4
decr 3
2

Cool! 我们通过在每个方法里面方法self对象自身来实现了漂亮的链式调用效果。

接下来我们尝试对内置字符串对象进行解构

# xyz.py
import firevalue = "hello"if __name__ == '__main__':fire.Fire()

字符串有upper和lower方法,我们反复使用upper和lower,然后观察结果

> python xyz.py value
hello
> python xyz.py value upper
HELLO
> python xyz.py value upper lower
Traceback (most recent call last):File "xyz.py", line 7, in <module>fire.Fire()File "/Users/pyloque/source/pys/.py/lib/python2.7/site-packages/fire/core.py", line 127, in Firecomponent_trace = _Fire(component, args, context, name)File "/Users/pyloque/source/pys/.py/lib/python2.7/site-packages/fire/core.py", line 366, in _Firecomponent, remaining_args)File "/Users/pyloque/source/pys/.py/lib/python2.7/site-packages/fire/core.py", line 542, in _CallCallableresult = fn(*varargs, **kwargs)
TypeError: upper() takes no arguments (1 given)

很不幸,内置的字符串对象似乎不支持链式调用,第一个upper倒是执行成功了。不过fire提供了一个特殊的符号用来解决这个问题。

> python xyz.py value upper - lower
hello
> python xyz.py value upper - lower - upper
HELLO
> python xyz.py value upper - lower - upper - lower
hello

减号用来表示参数的结束,这样后续的参数就不会被当成函数的参数来映射了。

总结

有了Google Fire这样一个小巧的类库,我们就可以从复杂的命令行参数分析中解脱出来了。我们常说写代码要漂亮优雅,没有好的类库,这种理想也不是非常容易实现的。如果没有fire,你有本事试试把复杂的命令行参数解析代码写优雅了给老师我看看。

640?wx_fmt=jpeg

高级文章,关注公众号「码洞」

这篇关于巧用Google Fire简化Python命令行程序的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/428892

相关文章

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

Python使用自带的base64库进行base64编码和解码

《Python使用自带的base64库进行base64编码和解码》在Python中,处理数据的编码和解码是数据传输和存储中非常普遍的需求,其中,Base64是一种常用的编码方案,本文我将详细介绍如何使... 目录引言使用python的base64库进行编码和解码编码函数解码函数Base64编码的应用场景注意

Python基于wxPython和FFmpeg开发一个视频标签工具

《Python基于wxPython和FFmpeg开发一个视频标签工具》在当今数字媒体时代,视频内容的管理和标记变得越来越重要,无论是研究人员需要对实验视频进行时间点标记,还是个人用户希望对家庭视频进行... 目录引言1. 应用概述2. 技术栈分析2.1 核心库和模块2.2 wxpython作为GUI选择的优

Python如何使用__slots__实现节省内存和性能优化

《Python如何使用__slots__实现节省内存和性能优化》你有想过,一个小小的__slots__能让你的Python类内存消耗直接减半吗,没错,今天咱们要聊的就是这个让人眼前一亮的技巧,感兴趣的... 目录背景:内存吃得满满的类__slots__:你的内存管理小助手举个大概的例子:看看效果如何?1.

Python+PyQt5实现多屏幕协同播放功能

《Python+PyQt5实现多屏幕协同播放功能》在现代会议展示、数字广告、展览展示等场景中,多屏幕协同播放已成为刚需,下面我们就来看看如何利用Python和PyQt5开发一套功能强大的跨屏播控系统吧... 目录一、项目概述:突破传统播放限制二、核心技术解析2.1 多屏管理机制2.2 播放引擎设计2.3 专

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

Python 中的异步与同步深度解析(实践记录)

《Python中的异步与同步深度解析(实践记录)》在Python编程世界里,异步和同步的概念是理解程序执行流程和性能优化的关键,这篇文章将带你深入了解它们的差异,以及阻塞和非阻塞的特性,同时通过实际... 目录python中的异步与同步:深度解析与实践异步与同步的定义异步同步阻塞与非阻塞的概念阻塞非阻塞同步