20

Google 开源的 Python 命令行库:深入 fire(一)

 4 years ago
source link: http://www.cnblogs.com/xueweihan/p/12121015.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

U7rMNzq.jpg!web

作者:HelloGitHub- Prodesire

HelloGitHub 的《讲解开源项目》系列,项目地址: https://github.com/HelloGitHub-Team/Article

一、前言

在第一篇“初探 fire”的文章中,我们初步掌握了使用 fire 的简单步骤,了解了它 Pythonic 的用法。

今天我们将深入了解 fire 的子命令、嵌套命令和属性访问功能。

本系列文章默认使用 Python 3 作为解释器进行讲解。
若你仍在使用 Python 2,请注意两者之间语法和库的使用差异哦~

二、功能

2.1 子命令

使用 fire 实现子命令有多种方式:

2.1.1 定义若干函数,使用 fire.Fire()

实现子命令最简单的方式就是定义若干个函数,每个函数名隐式就是子命令名称,然后调用 fire.Fire() 变将当前模块所有的函数解析为对应的子命令的处理函数。

import fire

def add(x, y):
  return x + y

def multiply(x, y):
  return x * y

if __name__ == '__main__':
  fire.Fire()

然后我们就可以在命令行中这么调用:

$ python example.py add 10 20
30
$ python example.py multiply 10 20
200

关于如何识别参数类型,比如上述 add 10 201020 是作为数字而非字符串,我们会在下篇文章的参数解析章节中进行讲解。

2.1.2 定义若干函数,使用 fire.Fire()

2.1.1 的版本中,会把所有函数都当做是子命令。有时我们可能只想把部分函数当做子命令,或者是希望子命令名称和函数名称不一样。这个时候我们就可以通过字典对象显式地告诉 fire

字典对象的形式为 {'子命令名称': 函数} ,比如前面的示例中,我们希望最终的子命令为 addmul ,那么就可以这么写:

fire.Fire({
  'add': add,
  'mul': multiply,
})

然后我们就可以在命令行中这么调用:

$ python example.py add 10 20
30
$ python example.py mul 10 20
200

2.1.3 定义类和方法,使用 fire.Fire()

定义类和方法的这种方式我们在上一篇文章中介绍过,它和定义函数的方式基本相同,只不过是用类的方式来组织。

然后将类实例化,并把实例化的对象多为 fire.Fire 的入参:

import fire

class Calculator(object):

  def add(self, x, y):
    return x + y

  def multiply(self, x, y):
    return x * y

if __name__ == '__main__':
  calculator = Calculator()
  fire.Fire(calculator)

2.1.4 定义类和方法,使用 fire.Fire()

2.1.3 中的唯一不同点是把类而非实例对象作为 fire.Fire 的入参:

fire.Fire(Calculator)

传递类和实例对象的基本作用是一样的,但传递类还有一个额外的特性:如果构造函数中定义了参数,那么这些参数都会作为整个命令行程序的选项参数。

import fire

class BrokenCalculator(object):

  def __init__(self, offset=1):
      self._offset = offset

  def add(self, x, y):
    return x + y + self._offset

  def multiply(self, x, y):
    return x * y + self._offset

if __name__ == '__main__':
  fire.Fire(BrokenCalculator)

查看帮助命令有:

$ python example.py --help
INFO: Showing help with the command 'example.py -- --help'.

NAME
    example.py

SYNOPSIS
    example.py <flags>

FLAGS
    --offset=OFFSET

由此可见构造函数 BrokenCalculator.__init__(self, offset=1) 中的 offset 自动转换为了命令行中的全局选项参数 --offset ,且默认值为 1

我们可以在命令行中这么调用:

$ python example.py add 10 20
31
$ python example.py multiply 10 20
201
$ python example.py add 10 20 --offset=0
30
$ python example.py multiply 10 20 --offset=0
200

2.2 命令组/嵌套命令

想要实现嵌套命令,可将多个类组织起来,示例如下:

class IngestionStage(object):

  def run(self):
    return 'Ingesting! Nom nom nom...'

class DigestionStage(object):

  def run(self, volume=1):
    return ' '.join(['Burp!'] * volume)

  def status(self):
    return 'Satiated.'

class Pipeline(object):

  def __init__(self):
    self.ingestion = IngestionStage()
    self.digestion = DigestionStage()

  def run(self):
    self.ingestion.run()
    self.digestion.run()

if __name__ == '__main__':
  fire.Fire(Pipeline)

在上面的示例中:

  • IngestionStage 实现了子命令 run
  • DigestionStage 实现了子命令 runstatus
  • Pipeline 的构造函数中将 IngestionStage 实例化为 ingestion ,将 DigestionStage 实例化为 digestion ,就将这两个放到一个命令组中,因而支持了:
    ingestion run
    digestion run
    digestion status
    
  • Pipeline 实现了子命令 run

因此整个命令行程序支持如下命令:

run
ingestion run
digestion run
digestion status

然后我们就可以在命令行中这么调用:

$ python example.py run
Ingesting! Nom nom nom...
Burp!
$ python example.py ingestion run
Ingesting! Nom nom nom...
$ python example.py digestion run
Burp!
$ python example.py digestion status
Satiated.

2.3 属性访问

属性访问fire 相对于其他命令行库来说一个比较独特的功能。所谓访问属性是获取预置的属性所对应的值。

举个例子,在命令行中指定 --code 来告知程序要查询的程序编码,然后希望通过 zipcode 属性返回邮编,通过 city 属性返回城市名。那么属性可实现为实例成员属性:

import fire

cities = {
  'hz': (310000, '杭州'),
  'bj': (100000, '北京'),
}


class City(object):

  def __init__(self, code):
    info = cities.get(code)
    self.zipcode = info[0] if info else None
    self.city = info[1] if info else None

if __name__ == '__main__':
  fire.Fire(City)

使用方式如下:

$ python example.py --code bj zipcode
100000
$ python example.py --code hz city
杭州

三、小结

使用 fire 实现子命令和嵌套命令相对于其他命令行库来说都更加简单清晰,不仅如此, fire 还提供了属性访问这种较为独特的能力。

在下篇文章中,我们将进一步深入了解 fire ,介绍其链式函数调用、自定义序列化、参数解析、fire 选项等更加高阶的功能。

『讲解开源项目系列』——让对开源项目感兴趣的人不再畏惧、让开源项目的发起者不再孤单。跟着我们的文章,你会发现编程的乐趣、使用和发现参与开源项目如此简单。欢迎留言联系我们、加入我们,让更多人爱上开源、贡献开源~


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK