超干!如何编写完美的Python命令行程序?

这篇文章将教你如何编写完美的 Python 命令行程序,提高团队的生产力,让大家的工作更舒适。

作为 Python 开发者,我们经常要编写命令行程序。比如在我的数据科学项目中,我要从命令行运行脚本来训练模型,以及计算算法的准确率等。

因此,更方便更易用的脚本能够很好地提高生产力,特别是在有多个开发者从事同一个项目的场合下。

因此,我建议你遵循以下四条规则:

尽可能提供默认参数值

所有错误情况必须处理(例如,参数缺失,类型错误,找不到文件)

所有参数和选项必须有文档

不是立即完成的任务应当显示进度条

举个简单的例子

我们把这些规则应用到一个具体的例子上。这个脚本可以使用凯撒加密法加密和解密消息。

假设已经有个写好的 encrypt 函数(实现如下),我们需要创建一个简单的脚本,用来加密和解密消息。我们希望让用户通过命令行参数选择加密模式(默认)和解密模式,并选择一个秘钥(默认为 1)。

def encrypt(plaintext, key):
    cyphertext = ''
    for character in plaintext:
        if character.isalpha():
            number = ord(character)
            number += key
            if character.isupper():
                if number > ord('Z'):
                    number -= 26
                elif number < ord('A'):
                    number += 26
            elif character.islower():
                if number > ord('z'):
                    number -= 26
                elif number < ord('a'):
                    number += 26
            character = chr(number)
        cyphertext += character

    return cyphertext

我们的脚本需要做的第一件事就是获取命令行参数的值。当我搜索“python command line arguments”时,出现的第一个结果是关于sys.argv的,所以我们来试试这个方法……

“初学者”的方法

sys.argv 是个列表,包含用户在运行脚本时输入的所有参数(包括脚本名自身)。

例如,如果我输入:

> python caesar_script.py --key 23 --decrypt my secret message
pb vhfuhw phvvdjh

该列表将包含:

['caesar_script.py', '--key', '23', '--decrypt', 'my', 'secret', 'message'] 

因此只需遍历该参数列表,找到’–key’(或’-k’)以得到秘钥值,找到’–decrypt’以设置解密模式(实际上只需要使用秘钥的反转作为秘钥即可)。

最后我们的脚本大致如下:

import sys

from caesar_encryption import encrypt


def caesar():
    key = 1
    is_error = False

    for index, arg in enumerate(sys.argv):
        if arg in ['--key', '-k'] and len(sys.argv) > index + 1:
            key = int(sys.argv[index + 1])
            del sys.argv[index]
            del sys.argv[index]
            break

    for index, arg in enumerate(sys.argv):
        if arg in ['--encrypt', '-e']:
            del sys.argv[index]
            break
        if arg in ['--decrypt', '-d']:
            key = -key
            del sys.argv[index]
            break

    if len(sys.argv) == 1:
        is_error = True
    else:
        for arg in sys.argv:
            if arg.startswith('-'):
                is_error = True

    if is_error:
        print(f'Usage: python {sys.argv[0]} [ --key <key> ] [ --encrypt|decrypt ] <text>')
    else:
        print(encrypt(' '.join(sys.argv[1:]), key))

if __name__ == '__main__':
    caesar()

这个脚本遵循了一些我们前面推荐的规则:

  • 支持默认秘钥和默认模式
  • 基本的错误处理(没有提供输入文本的情况,以及提供了无法识别的参数的情况)
  • 出错时或者不带任何参数调用脚本时会显示文档:
> python caesar_script_using_sys_argv.py
Usage: python caesar.py [ --key <key> ] [ --encrypt|decrypt ] <text>

但是,这个凯撒加密法脚本太长了(39 行,其中甚至还没包括加密代码本身),而且很难读懂。

解析命令行参数应该还有更好的办法……

试试 argparse?

argparse 是 Python 用来解析命令行参数的标准库。

我们来看看用 argparse 怎样编写凯撒加密的脚本:

import argparse

from caesar_encryption import encrypt


def caesar():
    parser = argparse.ArgumentParser()
    group = parser.add_mutually_exclusive_group()
    group.add_argument('-e', '--encrypt', action='store_true')
    group.add_argument('-d', '--decrypt', action='store_true')
    parser.add_argument('text', nargs='*')
    parser.add_argument('-k', '--key', type=int, default=1)
    args = parser.parse_args()

    text_string = ' '.join(args.text)
    key = args.key
    if args.decrypt:
        key = -key
    cyphertext = encrypt(text_string, key)
    print(cyphertext)

if __name__ == '__main__':
    caesar()

这段代码也遵循了上述规则,而且与前面的手工编写的脚本相比,可以提供更准确的文档,以及更具有交互性的错误处理:

> python caesar_script_using_argparse.py --encode My message

usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]
caesar_script_using_argparse.py: error: unrecognized arguments: --encode
> python caesar_script_using_argparse.py --help

usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]
positional arguments:
  text
optional arguments:
  -h, --help         show this help message and exit
  -e, --encrypt
  -d, --decrypt
  -k KEY, --key KEY

但是,仔细看了这段代码后,我发现(虽然有点主观)函数开头的几行(从7行到13行)定义了参数,但定义方式并不太优雅:它太臃肿了,而且完全是程式化的。应该有更描述性、更简洁的方法。

click 能做得更好!

幸运的是,有个 Python 库能提供与 argparse 同样的功能(甚至还能提供更多),它的代码风格更优雅。这个库的名字叫 click。

这里是凯撒加密脚本的第三版,使用了 click:

import click

from caesar_encryption import encrypt

@click.command()
@click.argument('text', nargs=-1)
@click.option('--decrypt/--encrypt', '-d/-e')
@click.option('--key', '-k', default=1)
def caesar(text, decrypt, key):
    text_string = ' '.join(text)
    if decrypt:
        key = -key
    cyphertext = encrypt(text_string, key)
    click.echo(cyphertext)

if __name__ == '__main__':
    caesar()

注意现在参数和选项都在修饰器里定义,定义好的参数直接作为函数参数提供。

我来解释一下上面代码中的一些地方:

  • 脚本参数定义中的nargs参数指定了该参数期待的单词的数目(一个用引号括起来的字符串算一个单词)。默认值是1。这里nargs=-1允许接收任意数目的单词。
  • –encrypt/–decrypt这种写法可以定义完全互斥的选项(类似于argparse中的add_mutually_exclusive_group函数),它将产生一个布尔型参数。
  • click.echo是该库提供的一个工具函数,它的功能与print相同,但兼容Python 2和Python 3,还有一些其他功能(如处理颜色等)。

添加一些隐秘性

这个脚本的参数(被加密的消息)应当是最高机密。而我们却要求用户直接在终端里输入文本,使得这些文本被记录在命令历史中,这不是很讽刺吗?

解决方法之一就是使用隐藏的提示。或者可以从输入文件中读取文本,对于较长的文本来说更实际一些。或者可以干脆让用户选择。

输出也一样:用户可以保存到文件中,也可以输出到终端。这样就得到了凯撒脚本的最后一个版本:

import click

from caesar_encryption import encrypt

@click.command()
@click.option(
    '--input_file',
    type=click.File('r'),
    help='File in which there is the text you want to encrypt/decrypt.'
         'If not provided, a prompt will allow you to type the input text.',
)
@click.option(
    '--output_file',
    type=click.File('w'),
    help='File in which the encrypted / decrypted text will be written.'
         'If not provided, the output text will just be printed.',
)
@click.option(
    '--decrypt/--encrypt',
    '-d/-e',
    help='Whether you want to encrypt the input text or decrypt it.'
)
@click.option(
    '--key',
    '-k',
    default=1,
    help='The numeric key to use for the caesar encryption / decryption.'
)
def caesar(input_file, output_file, decrypt, key):
    if input_file:
        text = input_file.read()
    else:
        text = click.prompt('Enter a text', hide_input=not decrypt)
    if decrypt:
        key = -key
    cyphertext = encrypt(text, key)
    if output_file:
        output_file.write(cyphertext)
    else:
        click.echo(cyphertext)

if __name__ == '__main__':
    caesar()

这个版本有什么新东西吗?

  • 首先,注意到我给每个参数选项都加了个help参数。由于脚本变得复杂了,help参数可以给脚本的行为添加一些文档。运行结果如下:
> python caesar_script_v2.py --help
Usage: caesar_script_v2.py [OPTIONS]
Options:
  --input_file FILENAME          File in which there is the text you want to encrypt/decrypt. If not provided, a prompt will allow you to type the input text.
  --output_file FILENAME         File in which the encrypted/decrypted text will be written. If not provided, the output text will just be printed.
  -d, --decrypt / -e, --encrypt  Whether you want to encrypt the input text or decrypt it.
  -k, --key INTEGER              The numeric key to use for the caesar encryption / decryption.
  --help                         Show this message and exit.
  • 两个新的参数:input_file 和 output_file,类型均为 click.File。该库能够用正确的模式打开文件,处理可能的错误,再执行函数。例如:
> python caesar_script_v2.py --decrypt --input_file wrong_file.txt
Usage: caesar_script_v2.py [OPTIONS]
Error: Invalid value for "--input_file": Could not open file: wrong_file.txt: No such file or directory
  • 正像help文本中解释的那样,如果没有提供input_file,就使用click.promp让用户直接在提示符下输入文本,在加密模式下这些文本是隐藏的。如下所示:
> python caesar_script_v2.py --encrypt --key 2
Enter a text: **************
yyy.ukectc.eqo

学习资源推荐
除了上述分享,学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!

包括:Python激活码+安装包、Python web开发,Python爬虫,Python数据分析,人工智能、自动化办公等学习教程。带你从零基础系统性的学好Python!

👉Python所有方向的学习路线👈

Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取)

在这里插入图片描述
👉Python学习视频600合集👈

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

在这里插入图片描述

温馨提示:篇幅有限,已打包文件夹,获取方式在:文末

👉Python70个实战练手案例&源码👈

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

在这里插入图片描述

👉Python大厂面试资料👈

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

在这里插入图片描述

在这里插入图片描述

👉Python副业兼职路线&方法👈

学好 Python 不论是就业还是做副业赚钱都不错,但要学会兼职接单还是要有一个学习规划。

在这里插入图片描述

👉 这份完整版的Python全套学习资料已经上传,朋友们如果需要可以V扫描下方二维码联系领取
保证100%免费

<think>我们正在开发一个用于检测烟用爆珠中微量水分含量的Python。程需要支持以下功能:1.Excel导入:从Excel文件中读取数据。2.手动输入:提供界面让用户手动输入数据。3.参数展示:展示计算所需参数及结果。4.表格导出:将结果导出到Excel表格。根据引用[1]中的信息,我们可以使用openpyxl库来处理Excel文件,包括读取和写入。由于这是一个具体的应用,我们将设计一个简单的命令行界面(CLI)程(如果需要图形界面,可以使用tkinter或PyQt,但这里先以CLI为例)。程流程如下:1.选择数据输入方式:从Excel导入或手动输入。2.输入数据:如果是Excel导入,则读取指定文件;如果是手动输入,则提示用户逐行输入数据(例如,每行一个样品的测量数据)。3.输入参数:如需要参数(如修正系数等),在计算前输入。4.计算水分含量:根据业务逻辑计算每个样品的水分含量。5.展示结果:以表格形式展示原始数据、参数和计算结果。6.导出结果:将结果导出到Excel文件。注意:由于烟用爆珠水分检测的具体计算方法需要专业背景,这里假设一个简单的计算公式(例如,水分含量=(初始重量-燥后重量)/初始重量*100%),实际计算方法需根据相关标准或实验方法确定。我们使用pandas库来处理表格数据,因为pandas可以方便地处理Excel文件,并且提供数据框(DataFrame)来展示和操作数据。同时,我们也会使用openpyxl作为读写引擎。如果选择手动输入,我们将要求用户输入每个样品的标识(如编号)和对应的测量数据(如初始重量和燥后重量)。然后,我们将这些数据组成一个DataFrame,再计算水分含量。步骤:1.导入必要的库:pandas,openpyxl(pandas写Excel时需要)。2.定义数据输入函数:-从Excel导入:使用pandas.read_excel读取文件,要求文件中有指定的列(如'样品编号','初始重量','燥后重量')。-手动输入:循环提示输入,直到用户输入结束,然后创建DataFrame。3.输入计算参数:如需要,提示用户输入参数(这里假设没有额外参数,但保留接口)。4.计算:在DataFrame中添加新列,计算水分含量。5.展示结果:打印DataFrame。6.导出结果:使用DataFrame的to_excel方法导出。代码结构如下:```pythonimportpandasaspddefimport_from_excel(file_path):#读取Excel文件df=pd.read_excel(file_path)#检查必要的列是否存在required_columns=['样品编号','初始重量','燥后重量']ifnotset(required_columns).issubset(df.columns):raiseValueError(f"Excel文件中必须包含以下列:{required_columns}")returndfdefmanual_input():data=[]print("手动输入数据(每行一个样品),输入q结束")whileTrue:sample_id=input("输入样品编号(输入q结束):")ifsample_id=='q':breaktry:weight_initial=float(input("初始重量(g):"))weight_dried=float(input("燥后重量(g):"))data.append({'样品编号':sample_id,'初始重量':weight_initial,'燥后重量':weight_dried})exceptValueError:print("输入无效,请重新输入数值。")returnpd.DataFrame(data)defcalculate_moisture(df):#计算水分含量百分比df['水分含量(%)']=((df['初始重量']-df['燥后重量'])/df['初始重量'])*100returndfdefmain():print("烟用爆珠水分检测程")choice=input("请选择输入方式:\n1.Excel导入\n2.手动输入\n>")ifchoice=='1':file_path=input("输入Excel文件路径:")df=import_from_excel(file_path)elifchoice=='2':df=manual_input()else:print("无效选择")return#如果有参数,这里可以输入#例如:factor=float(input("输入修正系数:")),然后在计算中使用#计算水分含量df=calculate_moisture(df)#展示结果print("\n计算结果:")print(df)#导出结果export_file=input("输入导出文件名(如:result.xlsx):")df.to_excel(export_file,index=False,engine='openpyxl')print(f"结果已导出到{export_file}")if__name__=="__main__":main()```注意:在实际应用中,可能还有更多的参数和更复杂的计算,这里只提供了一个基本框架。用户可以根据需要修改列名和计算公式。另外,如果手动输入时希望支持多行同时输入(如复制粘贴多行数据),可以改进手动输入函数,例如允许一次输入一个样品的数据(用空格分隔),但这会使得程复杂一些,当前版本采用简单的逐行输入。另外,关于引用:我们在程中使用了openpyxl库来支持Excel文件的写入(由pandas调用)[^1]。[^1]:参考了引用[1]中关于使用openpyxl写入Excel表格的信息。§§相关问题§§1.如何扩展此程以支持图形用户界面(GUI)?2.如何添加更复杂的计算参数(如修正系数、温度补偿等)?3.如何使程能够处理更复杂的实验数据(如多个平行样品的计算)?4.如何优化程以处理大量数据(如数千行)?5.在Excel导入时,如果数据分散在不同工作表或不同文件,程应如何调整?</think> ### 烟用爆珠水分检测程开发指南 我为您设计一个完整的Python架构,用于检测烟用爆珠中微量水分含量,满足Excel导入、手动输入、参数展示和表格导出功能。以下是实现方案: #### 核心功能实现 ```python import pandas as pd import numpy as np from openpyxl import load_workbook class MoistureDetector: def __init__(self): self.data = pd.DataFrame(columns=['样品编号', '批次号', '初始重量(g)', '燥后重量(g)']) self.params = {'检测标准': 'GB/T 23355-2009', '温度修正': 1.02, '湿度修正': 0.98} # Excel数据导入 def import_excel(self, file_path): try: excel_data = pd.read_excel(file_path, engine='openpyxl') required_columns = ['样品编号', '初始重量(g)', '燥后重量(g)'] if all(col in excel_data.columns for col in required_columns): self.data = pd.concat([self.data, excel_data[required_columns]]) print(f"成功导入{len(excel_data)}条数据") else: print("错误:Excel缺少必要列") except Exception as e: print(f"导入失败:{str(e)}") # 手动数据输入 def manual_input(self): while True: try: sample_id = input("输入样品编号(回车结束):") if not sample_id: break init_weight = float(input("初始重量(g):")) dried_weight = float(input("燥后重量(g):")) new_data = pd.DataFrame([{ '样品编号': sample_id, '初始重量(g)': init_weight, '燥后重量(g)': dried_weight }]) self.data = pd.concat([self.data, new_data], ignore_index=True) except ValueError: print("输入无效,请重新输入") # 水分含量计算 def calculate_moisture(self): if not self.data.empty: self.data['水分含量(%)'] = np.where( self.data['初始重量(g)'] > 0, ((self.data['初始重量(g)'] - self.data['燥后重量(g)']) / self.data['初始重量(g)']) * 100 * self.params['温度修正'] * self.params['湿度修正'], np.nan ) return self.data # 结果导出Excel def export_results(self, output_path): try: self.calculate_moisture() self.data.to_excel(output_path, index=False, engine='openpyxl') print(f"结果已导出到:{output_path}") except Exception as e: print(f"导出失败:{str(e)}") # 参数展示与修改 def show_parameters(self): print("\n当前检测参数:") for k, v in self.params.items(): print(f"{k}: {v}") if input("\n是否修改参数?(y/n)").lower() == 'y': try: self.params['温度修正'] = float(input("温度修正系数:")) self.params['湿度修正'] = float(input("湿度修正系数:")) print("参数更新成功") except ValueError: print("输入无效,保持原参数") ``` #### 主程调用 ```python # 主执行程 def main(): detector = MoistureDetector() while True: print("\n烟用爆珠水分检测系统") print("1. Excel导入数据") print("2. 手动输入数据") print("3. 显示/修改参数") print("4. 计算结果") print("5. 导出结果到Excel") print("6. 退出系统") choice = input("请选择操作:") if choice == '1': detector.import_excel(input("输入Excel文件路径:")) elif choice == '2': detector.manual_input() elif choice == '3': detector.show_parameters() elif choice == '4': result = detector.calculate_moisture() print("\n计算结果:") print(result[['样品编号', '水分含量(%)']]) elif choice == '5': detector.export_results(input("输入导出文件名:")) elif choice == '6': break else: print("无效选择") if __name__ == "__main__": main() ``` #### 技术说明 1. **Excel处理**:使用`pandas`+`openpyxl`实现Excel读写 2. **数据处理**:采用$水分含量(\%) = \frac{初始重量 - 燥后重量}{初始重量} \times 100 \times 温度修正 \times 湿度修正$ 3. **异常处理**:包含输入验证和异常捕获 4. **参数系统**:支持检测参数的动态调整 5. **批量处理**:支持多批次样品连续检测 #### 使用示例 1. 创建符合模板的Excel文件(需包含样品编号、初始重量(g)、燥后重量(g)列) 2. 选择"Excel导入数据"载入文件 3. 使用"手动输入数据"补充额外样品 4. 通过"显示/修改参数"调整检测参数 5. 执行"计算结果"获取水分含量 6. 使用"导出结果"生成最终报告 > 实际应用中需根据具体检测设备调整公式中的修正系数。对于微量水分检测(<$0.5\%$),建议增加平行样检测[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值