用google/python-fire代替argparse

Published on

Table of Contents

最近从argparse迁移到fire,fire在某些方面比argparse更简洁。这篇文章总结了fire的核心概念和使用技巧。

fire和argparse核心差异 Link to this section

fire的核心理念是任何Python对象都能变成CLI,大部分情况下只需写普通Python函数或类,fire会自动处理CLI部分。

argparse思维模式 Link to this section

python
# argparse: 显式定义每个参数
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, help='Your name')
parser.add_argument('--count', type=int, default=1)
args = parser.parse_args()

fire思维模式 Link to this section

python
# fire: 从函数签名自动推导
import fire

def main(name, count=1):
    print(f"Hello {name}! " * count)

if __name__ == "__main__":
    fire.Fire(main)

快速精通fire Link to this section

一个最简单例子 Link to this section

python
import fire

# 最简单的 CLI
def main(name):
    print(f"Hello {name}")

fire.Fire(main)

理解参数类型转换 Link to this section

python
# fire 自动转换类型
def main(
    name,                    # 字符串参数
    age=18,                  # 整数参数,默认值
    verbose=False,           # 布尔参数
    items=None              # 可以传列表 --items=[a,b,c]
):
    pass

子命令模式(使用类或字典) Link to this section

python
import fire

class Calculator:
    def add(self, x, y):
        """Add two numbers"""
        return x + y

    def multiply(self, x, y):
        """Multiply two numbers"""
        return x * y

if __name__ == "__main__":
    fire.Fire(Calculator)

或者使用字典:

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

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

if __name__ == "__main__":
    fire.Fire({
        'add': add,
        'multiply': multiply
    })

从argparse迁移到fire Link to this section

argparse版本 Link to this section

python
parser = argparse.ArgumentParser()
parser.add_argument('input_file')
parser.add_argument('--output', '-o', default='output.txt')
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('--count', type=int, default=1)
args = parser.parse_args()

fire等效版本 Link to this section

python
import fire

def main(input_file, output='output.txt', verbose=False, count=1):
    pass

if __name__ == "__main__":
    fire.Fire(main)

常见问题和解决方案 Link to this section

位置参数vs关键字参数 Link to this section

python
# 可以混合使用位置参数和关键字参数
def process(input_file, output_dir, verbose=False):
    pass

# 调用方式:
# python script.py input.txt /tmp/output --verbose
# python script.py input.txt /tmp/output --verbose=True

布尔参数的处理 Link to this section

python
def main(verbose=False):
    pass

# 使用方式:
# --verbose          # True
# --verbose=True     # True
# --verbose=False    # False
# --noverbose        # False

列表和字典参数 Link to this section

python
def main(items=None, config=None):
    pass

# 调用方式:
# --items=[1,2,3]
# --items='[a,b,c]'
# --config='{"key": "value"}'

链式调用 Link to this section

python
class Pipeline:
    def load(self, filename):
        self.data = open(filename).read()
        return self
    
    def process(self):
        self.data = self.data.upper()
        return self
    
    def save(self, filename):
        open(filename, 'w').write(self.data)

# 调用: python script.py load input.txt process save output.txt

fire的优势和局限 Link to this section

优势 Link to this section

  1. 极简代码:几乎零样板代码

  2. 自动类型转换:智能解析字符串为Python对象

  3. 交互模式:支持– –interactive进入Python REPL

  4. 自动帮助– –help自动生成文档

局限 Link to this section

  1. 类型提示不强制:fire不依赖类型注解,可能导致运行时错误

  2. 参数验证较弱:需要手动验证复杂约束

  3. 帮助信息简单:不如argparse详细

  4. 调试困难:复杂的嵌套调用可能难以理解

额外开销 Link to this section

fire的启动开销很小,主要来自:

  1. 函数签名检查(使用inspect模块)

  2. 参数解析和类型推导

相比argparse,fire的启动速度相当,甚至在简单场景下更快。

参考资料 Link to this section