翼度科技»论坛 编程开发 python 查看内容

Python Fire:自动生成命令行接口

4

主题

4

帖子

12

积分

新手上路

Rank: 1

积分
12
命令行程序是平时写一些小工具时最常用的方式。
为了让命令行程序更加灵活,我们常常会设置一些参数,根据参数让程序执行不同的功能。
这样就不用频繁的修改代码来执行不同的功能。
随着命令行程序功能的丰富,也就是参数多了以后,解析和管理参数之间的关系会变得越来越繁重。
而本次介绍的 Fire 库,正好可以解决这个问题。
使用 Fire 库,我们只要关心具体功能的实现,最后Fire会帮助我们自动把所有功能组织成一个命令行程序。
Fire库在github上的地址:https://github.com/google/python-fire
1. 一般命令

一般的命令,也就是带有几个参数的一段程序,比如:
  1. # -*- coding:utf-8 -*-
  2. def import_file(fp):
  3.     print("import file from: {}".format(fp))
  4. def export_file(fp):
  5.     print("EXPORT file to: {}".format(fp))
复制代码
这是模拟文件导出功能的两个函数。
使用 Fire 转换成命令行程序非常简单,下面介绍几种常用的方式。
1.1. 默认方式
  1. # -*- coding:utf-8 -*-
  2. import fire
  3. def import_file(fp):
  4.     print("IMPORT file from: {}".format(fp))
  5. def export_file(fp):
  6.     print("EXPORT file to: {}".format(fp))
  7. if __name__ == "__main__":
  8.     # fire默认会将所有函数转换成子命令
  9.     fire.Fire()
复制代码
然后,就可以通过子命令的方式执行导入导出功能。
  1. $ python main.py import_file --fp ./path/xxx.csv
  2. IMPORT file from: ./path/xxx.csv
  3. $ python main.py export_file --fp ./path/xxx.csv
  4. EXPORT file to: ./path/xxx.csv
复制代码
函数的名称自动变为子命令的名称函数的参数自动变成子命令的参数
1.2. Fire

Fire库的默认方式会把所有的函数都转换为子命令,
如果只想导出一个函数的话,可以用 Fire的方式。
  1. if __name__ == "__main__":
  2.     # 只导出 import_file 函数作为子命令
  3.     fire.Fire(import_file)
复制代码
只导出一个函数的时候,执行命令的时候不需要输入子命令的名称。
  1. $ python main.py --fp ./path/xxx.csv
  2. IMPORT file from: ./path/xxx.csv
复制代码
1.3. Fire

导出多个函数作为子命令时,默认是使用函数名作为子命令名称的,函数名称有时候会非常长,输入很麻烦。
这时,可以用 Fire 方式。
  1. if __name__ == "__main__":
  2.     # 子命令的名称分别是:import 和 export
  3.     fire.Fire({
  4.         "import": import_file,
  5.         "export": export_file,
  6.     })
复制代码
执行时,使用简化的子命令名称。
  1. $ python main.py import --fp ./path/xxx.csv
  2. IMPORT file from: ./path/xxx.csv
  3. $ python main.py export --fp ./path/xxx.csv
  4. EXPORT file to: ./path/xxx.csv
复制代码
这种方式非常灵活,不仅可以设置子命令名称,还可以控制需要导出哪些函数。
1.4. Fire

除了导出函数,Fire方式也可以导出对象的公有方法。
  1. import fire
  2. class FileHandler():
  3.     def __init__(self):
  4.         pass
  5.     def import_file(self, fp):
  6.         print("IMPORT file from: {}".format(fp))
  7.     def export_file(self, fp):
  8.         print("EXPORT file to: {}".format(fp))
  9. if __name__ == "__main__":
  10.     fh = FileHandler()
  11.     fire.Fire(fh)
复制代码
使用方式如下:
  1. $ python main.py import_file --fp ./path/xxx.csv
  2. IMPORT file from: ./path/xxx.csv
  3. $ python main.py export_file --fp ./path/xxx.csv
  4. EXPORT file to: ./path/xxx.csv
复制代码
使用对象的方式没有直接使用函数那么简单,但有个好处是可以在初始化时传入一些状态。
  1. import fire
  2. import os
  3. class FileHandler():
  4.     def __init__(self, folder=""):
  5.         self.folder = folder
  6.     def import_file(self, fp):
  7.         print("IMPORT file from: {}".format(os.path.join(self.folder, fp)))
  8.     def export_file(self, fp):
  9.         print("EXPORT file to: {}".format(os.path.join(self.folder, fp)))
  10. if __name__ == "__main__":
  11.     # 设置了默认文件夹,使用时直接传入文件名即可
  12.     fh = FileHandler("./default_path")
  13.     fire.Fire(fh)
复制代码
  1. $ python main.py import_file --fp xxx.csv
  2. IMPORT file from: ./default_path/xxx.csv
  3. $ python main.py export_file --fp xxx.csv
  4. EXPORT file to: ./default_path/xxx.csv
复制代码
1.5. Fire

Fire的方式也可以直接作用在类上,不用初始化对象。
  1. if __name__ == "__main__":
  2.     fire.Fire(FileHandler)
复制代码
和 Fire 不同的是,__init__方法的参数也变成了命令的参数,也可以在命令行中调整了。
  1. $ python main.py import_file --fp xxx.csv
  2. IMPORT file from: xxx.csv
  3. $ python main.py import_file --fp xxx.csv --folder ./my_folder
  4. IMPORT file from: ./my_folder/xxx.csv
复制代码
2. 组合命令

当功能越来越多时,可能就会需要组合一些功能一起运行,省得输入一个一个的子命令。
  1. class FileHandler():
  2.     def __init__(self, folder="./defalut_dir"):
  3.         self.folder = folder
  4.     def import_file(self, fp):
  5.         print("IMPORT file from: {}".format(os.path.join(self.folder, fp)))
  6.     def export_file(self, fp):
  7.         print("EXPORT file to: {}".format(os.path.join(self.folder, fp)))
  8. class DatabaseHandler():
  9.     def __init__(self, src="aliyun-mysql", dst="tecent-mysql"):
  10.         self.src = src
  11.         self.dst = dst
  12.     def import_db(self):
  13.         print("IMPORT data from: {} to: {}".format(self.src, self.dst))
  14.     def export_db(self):
  15.         print("EXPORT data from: {} to: {}".format(self.src, self.dst))
  16. # 组合 FileHandler 和 DatabaseHandler
  17. class ComposeHandler():
  18.     def __init__(self):
  19.         self.fh = FileHandler()
  20.         self.dh = DatabaseHandler()
  21.     def import_all(self, fp):
  22.         self.fh.import_file(fp)
  23.         self.dh.import_db()
  24.     def export_all(self, fp):
  25.         self.fh.export_file(fp)
  26.         self.dh.export_db()
  27. if __name__ == "__main__":
  28.     fire.Fire(ComposeHandler)
复制代码
导出组合命令之后,不仅可以执行组合命令,也可以只执行子命令。
  1. $ python main.py import_all --fp xxx.csv
  2. IMPORT file from: ./defalut_dir/xxx.csv
  3. IMPORT data from: aliyun-mysql to: tecent-mysql
  4. $ python main.py export_all --fp xxx.csv
  5. EXPORT file to: ./defalut_dir/xxx.csv
  6. EXPORT data from: aliyun-mysql to: tecent-mysql
  7. $ python main.py fh export_file --fp xxx.csv
  8. EXPORT file to: ./defalut_dir/xxx.csv
  9. $ python main.py dh export_db
  10. EXPORT data from: aliyun-mysql to: tecent-mysql
复制代码
3. 链式命令

链式命令和组合命令不一样的地方在于:
组合命令中,每个命令之间一般是相互独立的,
而链式命令中,上一个命令的执行结果会对下一个命令造成影响。
比如:
  1. class Stat():
  2.     def __init__(self):
  3.         self.total = 0
  4.         self.avg = 0
  5.         self.n = 0
  6.     # 模拟统计合计值
  7.     def sum(self, n):
  8.         self.n += n
  9.         for i in range(n):
  10.             self.total += i
  11.         return self
  12.     # 模拟求平均值
  13.     def average(self):
  14.         if self.n == 0:
  15.             self.avg = 0
  16.         else:
  17.             self.avg = self.total / self.n
  18.         return self
  19.     # 显示分析结果
  20.     def show(self):
  21.         print("SUM: {}, and AVERAGE: {}".format(self.total, self.avg))
  22. if __name__ == "__main__":
  23.     fire.Fire(Stat)
复制代码
执行链式命令时,可以先求和,再求平均值,最后显示结果:
  1. $ python main.py sum 10 average show
  2. SUM: 45, and AVERAGE: 4.5
复制代码
因为是链式命令,所以可以多次执行:
  1. $ python main.py sum 10 sum 10 average show
  2. SUM: 90, and AVERAGE: 4.5
  3. $ python main.py sum 10 sum 20 sum 30 average show
  4. SUM: 670, and AVERAGE: 11.166666666666666
复制代码
4. 复杂命令参数

上面的示例中,参数都是简单的数据类型,比如字符串,数字之类的。
最后,介绍下复杂的命令参数如何使用,所谓复杂的参数,就是元组,列表,字典等等。
  1. def hello(data):
  2.     tp = type(data).__name__
  3.     if  tp == "tuple" or tp == "list":
  4.         for item in data:
  5.             print("hello: {}".format(item))
  6.     if tp == "dict":
  7.         for k, v in data.items():
  8.             print("hello: key {}, val {}".format(k, v))
  9. if __name__ == "__main__":
  10.     fire.Fire(hello)
复制代码
python是弱类型语言,函数的参数可以是任何类型。
主要看看命令行中如何传入复杂的类型:
  1. $ python main.py "(aa, bb, cc)"
  2. hello: aa
  3. hello: bb
  4. hello: cc
  5. $ python main.py "[aa, bb, cc]"
  6. hello: aa
  7. hello: bb
  8. hello: cc
  9. $ python main.py "{aa: 11, bb: 22}"
  10. hello: key aa, val 11
  11. hello: key bb, val 22
复制代码
5. 总结

Python的Fire库是一个构思非常巧妙的命令行接口库,各种语言的命令行接口我接触过不少,还没有在其他编程语言中看到过类似的库。
Fire库最方便的地方在于,你不用再关心命令行的参数(参数的解析一直是命令行程序中最让人头疼的地方),只要专注于自己要实现的功能。
此外,如果你已经有一些python脚本的话,通过这个库把它们改造成命令行程序也非常简单。

来源:https://www.cnblogs.com/wang_yb/p/18000668
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具