将 argparse 转义字符作为选项处理

Handling argparse escaped character as option

argparse 库处理转义字符(如 \t 到制表符和 \n 到换行符)的方式与我喜欢的不同。 An answer to this question 给出了一个解决方案,但我想让它对用户不那么明显。

给定程序:

#!/usr/bin/env python3
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-d', '--delimiter', default='\t')
args = parser.parse_args()
print(args)

您将收到此输出:

bash$ parser.py -d \t
Namespace(delimiter='t')

bash$ parser.py -d \t
Namespace(delimiter='\t')

bash$ parser.py -d '\t'
Namespace(delimiter='\t')

bash$ parser.py -d '\t'
Namespace(delimiter='\\t')

bash$ parser.py -d "\t"
Namespace(delimiter='\t')

bash$ parser.py -d "\t"
Namespace(delimiter='\t')

bash$ parser.py -d $'\t'
Namespace(delimiter='\t')

bash$ parser.py -d $'\t'
Namespace(delimiter='\t')

bash$ parser.py -d $"\t"
Namespace(delimiter='$\t')

bash$ parser.py -d $"\t"
Namespace(delimiter='$\t')

我只用

得到了想要的参数
parser.py -d $'\t'

但我希望输入看起来像

parser.py -d \t 

或更少

parser.py -d '\t'
parser.py -d "\t"

如果我想改变行为,我可以使用 argparse 库来做这件事吗? 如果没有,我是否可以在现有的 argparse 库之上编写行为? 如果不是,这就是 bash 将参数传递给 argparse 的方式,因此不在我的手中吗? 如果这是真的,这是否通常记录给用户,或者这种行为是否被认为是正常的?

就我个人而言,我只希望这种行为——你的 shell 解释一些项目并传递一个文字制表符,或者一个反斜杠和一个字母 t——而不一定希望 Python 程序进行二级解释(argparse 中没有任何内容可以执行此操作)。

尽管如此,Python 已经为此内置了解释器;参见 this question and answers

您在 namespace 中看到的字符串正是出现在 sys.argv 中的字符串 - 它是由 bash 和解释器创建的。 parser 不处理或调整此字符串。它只是设置 namespace 中的值。您可以在解析之前通过打印 sys.argv 来验证这一点。

如果你很清楚用户想要什么,那么我建议解析后修改args.delimiter。解析器的主要目的是弄清楚用户想要什么。作为程序员,您可以以任何方式解释和应用该信息。

一旦你计算出一个令人满意的 post 解析函数,你可以将它实现为这个参数的 type(比如 int()float()为数字字符串做)。但重点放在post-解析处理上。

假设问题部分是关于如何执行@hpaulj 解释的 post-processing 并且由于我在上面的链接中看不到 Python 3 的立即解决方案,这里是快速解决方案:

import codecs

def unescaped_str(arg_str):
    return codecs.decode(str(arg_str), 'unicode_escape')

然后在解析器中:

parser.add_argument('-d', '--delimiter', type=unescaped_str, default='\t')

这将使您不太理想的案例有效:

parser.py -d '\t'
parser.py -d "\t"

但不是所需的未转义 \t。无论如何,由于没有检查机制,此解决方案可能很危险...

这是正确处理引用('\t'"\t")输入案例的快速方法(尽管它只专门处理您的特定制表符案例输入):

parser.add_argument('-d', '--delimiter', type=lambda d: '\t' if d == '\t' else d)

首先注意以下几点:在Python中,"\t"是tab字面量,转义后的"\t"是一个双字符的字符串(第一个字符是"\",第二个是"t")。你可以用 len("\t"), len("\t") 检查这个,它给出 1, 2.

当用户在命令行中输入 -d '\t' 时,python 将收到字符串 '\t'(字面意思是两个字符,"backslash" 和 "t").我们想用一个 "tab" 字符替换这个由两个字符组成的字符串。 type 参数采用函数作为预处理参数的方式。 lambda 函数检查两个字符的字符串并将其替换为制表符。