如何将Argparse参数的默认值设置为位置参数的值?

8 投票
3 回答
21268 浏览
提问于 2025-04-18 18:26

我有一个Python脚本,用来发送GET请求。它使用Argparse这个工具来接收三个参数:

  1. 地址:发送GET请求的目标地址
  2. 主机:在GET请求中声明的主机名
  3. 资源:请求的具体资源

一个使用示例可能是:

$ python get.py 198.252.206.16 stackoverflow.com /questions/ask

不过在大多数情况下,其实只需要提供主机和资源,因为主机会自动解析成地址:

 $ host -t a stackoverflow.com
 stackoverflow.com has address 198.252.206.16

所以理想的使用方式可能是:

$ python get.py stackoverflow.com /questions/ask

我该如何设置Argparse,让地址参数的默认值等于主机参数的值呢?


有人让我展示一下当前解析参数的代码。代码如下:

import argparse

parser = argparse.ArgumentParser(description=
         'Send a GET request and obtain the HTTP response header and/or body.')

parser.add_argument("-v", "--verbose",
                    help="Turn on verbose mode.",
                    action="store_true")
parser.add_argument("-p", "--port",
                    type=int,
                    default=80,
                    help="Which port to use when sending the GET request."
parser.add_argument("address",
                    help="Where to send the GET request to.")
parser.add_argument("host",
                    help="Which Host to declare in the GET request.")   
parser.add_argument("resource",
                    help="Which resource to request.")

parser.parse_args()

3 个回答

1

这里有两个不错的选择,你可以根据自己对使用位置参数和选项参数的偏好来决定。

  1. 添加一个位置参数,使用 nargs='+'。解析参数后,检查这个列表的长度。如果长度是3,就把它们分别设置为地址、主机和资源。如果长度是2,就把主机和资源设置好,然后把地址从主机复制过来。否则,就打印一个使用说明并抛出一个异常。

  2. 把主机和资源保留为位置参数,把地址改成一个选项。这意味着命令行的格式会变成这样:

python get.py stackoverflow.com /questions/ask --address 198.252.206.16

你可以把默认值设置为 None,然后在解析参数后检查它是否为 None。我觉得在 argparse 中没有办法让它自动默认到另一个位置参数。

选择哪个完全是个人喜好,我更喜欢第二种(可选参数本质上就是选项),因为这样更符合工具的使用。如果你强行让 argparse 像第一种那样工作,你还得额外覆盖 --help 选项、使用说明等,因为自动生成的默认值会让人困惑。

3

如果你想仅仅通过位置参数来实现你描述的功能,可以使用一个列表参数(nargs),像这样:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("args", nargs="+")
parsed = parser.parse_args()
args = parsed.args
if len(args) == 3:
    ip, host, address = args
    print ip, host, address
elif len(args) == 2:
    ip, host, address = args[0], args[0], args[1]
    print ip, host, address
else:
    print "Invalid args"

不过,这种方法有点不太正规,而且你还会失去 argparse 提供的一些好处(你需要手动检查参数)。我建议你使用可选参数。可以像这样:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-host", required=True)
parser.add_argument("-res", required=True)
parser.add_argument("-ip", required=False)
args = parser.parse_args()

ip = args.ip if args.ip else args.host
print args.host, args.res, ip

然后像这样执行它:

python2.7 test.py -host hello -res world
16

nargs='?' 很好地处理了这种情况。如果只有两个字符串,它们会被分别赋值给 hostresource,而 address 则会使用默认值(None)。如果有三个字符串,它们就会分别赋值给这三个变量。如果你觉得这样不太好理解,可以想象一下 ?re 模式中的表现。

在解析器完成后,很容易将 host 的值赋给 address。在 parse_args 中尝试进行这种赋值是没有意义的,因为此时还不知道 host 的值。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("address", nargs='?')
parser.add_argument("host")
parser.add_argument("resource")
args = parser.parse_args()

if args.address is None:
    args.address = args.host
print(args)

用法是:

usage: get.py [-h] [address] host resource

其中 [] 清晰地标记了这个可选的位置参数。

撰写回答