将 argparse 与采用 **kwargs 参数的函数一起使用 [英] Using argparse with function that takes **kwargs argument

查看:23
本文介绍了将 argparse 与采用 **kwargs 参数的函数一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 argparse 获取输入并将其传递给一个函数,该函数将两个变量和 **kwargs 作为参数.

这是我的功能:

导入请求导入系统导入参数解析def location_by_coordinate(LAT, LNG, **kwargs):如果不是 kwargs:坐标网址 = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)r = requests.get(coordinate_url).text别的:坐标网址 = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)对于键,kwargs.iteritems() 中的值:如果以 kwargs 为单位的距离":距离 = kwargs.get('DISTANCE')如果距离>5000:打印距离打印最大距离为 5000 米,值重新分配为默认值 1000 米"距离 = 1000坐标网址 = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)r = requests.get(coordinate_url).text别的:经过坐标网址 = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)r = requests.get(coordinate_url).text如果 kwargs 中的FACEBOOK_PLACES_ID":fb_places_id = kwargs.get('FACEBOOK_PLACES_ID')有效载荷 = {'FACEBOOK_PLACES_ID': '%s' % (fb_places_id), 'DISTANCE': '%s' % (DISTANCE)}r = requests.get(coordinate_url, params=payload).text如果 kwargs 中的FOURSQUARE_ID":Foursquare_id = kwargs.get('FOURSQUARE_ID')有效载荷 = {'FOURSQUARE_ID': '%s' % (foursquare_id), 'DISTANCE': '%s' % (DISTANCE)}r = requests.get(coordinate_url, params=payload).text如果 kwargs 中的FOURSQUARE_V2_ID":Foursquare_v2_id = kwargs.get('FOURSQUARE_V2_ID')有效载荷 = {'FOURSQUARE_V2_ID': '%s' % (foursquare_v2_id), 'DISTANCE': '%s' % (DISTANCE)}r = requests.get(coordinate_url, params=payload).text#打印r返回

鉴于此函数及其对 **kwargs 的使用,我应该如何设置子解析器?

到目前为止,我是这样设置命令行解析器的:

 def main():parser = argparse.ArgumentParser(description="API Endpoints tester")subparsers = parser.add_subparsers(dest="command", help="可用命令")location_by_parser = subparsers.add_parser("location_by_coordinate", help="位置函数")location_by_parser.add_argument("LAT", help="纬度")location_by_parser.add_argument("LNG", help="longitude")参数 = parser.parse_args(sys.argv[1:])参数 = vars(参数)命令 = arguments.pop("命令")如果命令==location_by_coordinate":LAT, LNG = location_by_coordinate(**参数)别的:打印未提供命令..."如果 __name__ == "__main__":主要的()

显然,当我像这样在命令行中调用上面的 main() 函数时,它与 location_by_coordinate() 函数配合得很好:

$ python argstest.py location_by_coordinate 40.5949799 -73.9495148

但是使用当前的代码,如果我尝试:

$ python argstest.py location_by_coordinate 40.5949799 -73.9495148 DISTANCE=3000

显然,我得到:

argstest.py:错误:无法识别的参数:DISTANCE=3000

但我不确定如何为 **kwargs 设置子解析器.如果我尝试设置这样的子解析器:

location_by_parser.add_argument("**kwargs", help="**kwargs")

然后再次尝试该命令:

$ python argstest.py location_by_coordinate 40.5949799 -73.9495148 DISTANCE=3000

这不起作用,因为 arguments 对象(它是一个字典)变成了这样:

{'LAT':'40.5949799','LNG':'-73.9495148','command':'location_by_coordinate','**kwargs':'DISTANCE=3000'}

返回此回溯:

回溯(最近一次调用最后一次): 中的文件argstest.py",第 118 行主要的()文件argstest.py",第 108 行,在主目录中foo = location_by_coordinate(**参数)文件argstest.py",第 40 行,位于 location_by_coordinate返回UnboundLocalError:赋值前引用了局部变量r"

如何启用 argparse 来处理/解析在命令行中输入的内容,这些内容旨在通过 **kwargs 传递给函数?

解决方案

你了解

{'LAT': '40.5949799', 'LNG': '-73.9495148', 'command': 'location_by_coordinate', '**kwargs': 'DISTANCE=3000'}

arguments 字典?您使用 '**kwargs' 的名称 ('dest') 定义了一个 'positional' 参数.您也可以将其命名为foobar".解析器将字符串 'DISTANCE=3000' 分配给 args 命名空间中的该属性,该属性在 arguments 中变成了字典键:值对.

当然,您可以查找 arguments['**kwargs'],然后自己解析该值:

v = arguments['**kwargs'] # 如果你愿意,也可以弹出如果 v 不是 None:k, v = v.split('=')参数[k] = int(v)

可以概括为处理多个对(用 `nargs='*' 定义).

<小时>

argparse 不像 Python 函数那样处理参数,所以没有什么与 **kwargs 完全类似.

接受诸如 distance 之类的东西的正常方法是使用可选"或标记参数.

parser.add_argument('-d','--distance', type=int, help=...)

哪个会接受

python argstest.py location_by_coordinate 40.5949799 -73.9495148 --distance=3000python argstest.py location_by_coordinate 40.5949799 -73.9495148 --distance 3000python argstest.py location_by_coordinate 40.5949799 -73.9495148 --d3000python argstest.py location_by_coordinate 40.5949799 -73.9495148

也可以设置为使用 --DISTANCE 或其他名称.在最后一种情况下,args 命名空间将具有 distance 的默认值.默认默认值为 None.

这是将 kwarg 之类的参数添加到 argparse 的直接方法.

接受像对这样的任意字典,distance:3000distance=3000,之前已经在SO上被问过.答案一直是我在上面勾画的解析的一些变体.它可以在自定义 Action 类中完成,也可以按照我的建议进行解析.

哎呀,这个答案几乎是我几天前写的一个答案的克隆:https://stackoverflow.com/a/33639147/901925

一个类似的 2011 年问题:使用argparse解析arg=val"形式的参数

Python argparse dict arg

==================================

(编辑)

使用 *args 的函数示例:

在[2]中:导入argparse在 [3]: def foo(*args, **kwargs):...:打印('args',args)...:打印('kwargs',kwargs)...:在 [4]: parser=argparse.ArgumentParser()在 [5] 中:parser.add_argument('arg1')在 [6]: parser.add_argument('arg2',nargs='+')在[7]中:args=parser.parse_args('一二三'.split())在 [8] 中:参数Out[8]: Namespace(arg1='one', arg2=['two', 'three'])

所以我有 2 个位置参数,一个带有单个字符串值,另一个带有列表(由于 + nargs).

使用这些 args 属性调用 foo:

在 [10]: foo(args.arg1)args ('一个',)夸格{}在 [11]: foo(args.arg1, args.arg2)args ('一', ['二', '三'])夸格{}在 [12]: foo(args.arg1, arg2=args.arg2)args ('一个',)kwargs {'arg2': ['二', '三']}

我定义了位置",但它与可选"一样有效.位置和可选之间的区别在命名空间中消失了.

如果我将命名空间转换为字典,我可以通过各种方式将值传递给 foo,通过 *args 或通过 **kwargs.这完全取决于我如何调用 foo,而不是它们如何出现在 argsarguments 中.这些都不是 argparse 独有的.

在[13]中:arguments = vars(args)在 [14] 中:参数出[14]:{'arg2':['二','三'],'arg1':'一'}在 [15]: foo(arguments['arg2'],arguments['arg1'])args (['二', '三'], '一')夸格{}在 [16]: foo(arguments['arg2'],arguments)args (['二', '三'], {'arg2': ['二', '三'], 'arg1': '一'})夸格{}在 [17]: foo(arguments['arg2'], **arguments)args (['二', '三'],)kwargs {'arg2':['二','三'],'arg1':'一'}在 [24]: foo(*arguments, **arguments)args('arg2', 'arg1') # *args 是参数的键kwargs {'arg2':['二','三'],'arg1':'一'}在 [25]: foo(*arguments.values(), **arguments)args (['two', 'three'], 'one') # *args 是参数的值kwargs {'arg2':['二','三'],'arg1':'一'}

I'm using argparse to take input and pass it to a function that takes as arguments two variables and **kwargs.

Here's my function:

import requests
import sys
import argparse


def location_by_coordinate(LAT, LNG, **kwargs):
    if not kwargs:
        coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
        r = requests.get(coordinate_url).text
    else:
        coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
        for key, value in kwargs.iteritems():
            if 'DISTANCE' in kwargs:
                distance = kwargs.get('DISTANCE')
                if distance > 5000:
                    print distance
                    print "max distance is 5000m, value is reassigned to default of 1000m"
                    distance = 1000
                    coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
                    r = requests.get(coordinate_url).text
                else:
                    pass
                    coordinate_url = "https://api.instagram.com/v1/locations/search?lat=%s&lng=%s&access_token=%s" % (LAT, LNG, current_token)
                    r = requests.get(coordinate_url).text
            if 'FACEBOOK_PLACES_ID' in kwargs:
                fb_places_id = kwargs.get('FACEBOOK_PLACES_ID')
                payload = {'FACEBOOK_PLACES_ID': '%s' % (fb_places_id), 'DISTANCE': '%s' % (DISTANCE)}
                r = requests.get(coordinate_url, params=payload).text
            if 'FOURSQUARE_ID' in kwargs:
                foursquare_id = kwargs.get('FOURSQUARE_ID')
                payload = {'FOURSQUARE_ID': '%s' % (foursquare_id), 'DISTANCE': '%s' % (DISTANCE)}
                r = requests.get(coordinate_url, params=payload).text
            if 'FOURSQUARE_V2_ID' in kwargs:
                foursquare_v2_id = kwargs.get('FOURSQUARE_V2_ID')
                payload = {'FOURSQUARE_V2_ID': '%s' % (foursquare_v2_id), 'DISTANCE': '%s' % (DISTANCE)}
                r = requests.get(coordinate_url, params=payload).text
    #print r
    return r

Given this function and its use of **kwargs, how should I setup the subparsers?

Here's how I've setup the command line parser thus far:

 def main():
        parser = argparse.ArgumentParser(description="API Endpoints tester")
        subparsers = parser.add_subparsers(dest="command", help="Available commands")

        location_by_parser = subparsers.add_parser("location_by_coordinate", help="location function")
        location_by_parser.add_argument("LAT", help="latitude")
        location_by_parser.add_argument("LNG", help="longitude")

        arguments = parser.parse_args(sys.argv[1:])
        arguments = vars(arguments)
        command = arguments.pop("command")
        if command == "location_by_coordinate":
            LAT, LNG = location_by_coordinate(**arguments)
        else:
            print "No command provided..."

    if __name__ == "__main__":
        main()

Obviously, the above main() function works fine with the location_by_coordinate() function when I call it at the command line like this:

$ python argstest.py location_by_coordinate 40.5949799 -73.9495148

But with the code the way it is currently, if I try:

$ python argstest.py location_by_coordinate 40.5949799 -73.9495148 DISTANCE=3000

Obviously, I get:

argstest.py: error: unrecognized arguments: DISTANCE=3000

But I'm not sure how to setup a subparser for **kwargs. If I try to setup a subparser like this:

location_by_parser.add_argument("**kwargs", help="**kwargs")

and then try that command again:

$ python argstest.py location_by_coordinate 40.5949799 -73.9495148 DISTANCE=3000

That doesn't work because the arguments object (which is a dictionary), becomes this:

{'LAT': '40.5949799', 'LNG': '-73.9495148', 'command': 'location_by_coordinate', '**kwargs': 'DISTANCE=3000'}

And this Traceback is returned:

Traceback (most recent call last):
  File "argstest.py", line 118, in <module>
    main()
  File "argstest.py", line 108, in main
    foo = location_by_coordinate(**arguments)
  File "argstest.py", line 40, in location_by_coordinate
    return r
UnboundLocalError: local variable 'r' referenced before assignment

How can I enable argparse to handle/to parse what is entered at the command line that is intended to be passed to the function via **kwargs?

解决方案

Do you understand what is going on with the

{'LAT': '40.5949799', 'LNG': '-73.9495148', 'command': 'location_by_coordinate', '**kwargs': 'DISTANCE=3000'}

arguments dictionary? You defined a 'positional' argument with the name ('dest') of '**kwargs'. You could just as well named it 'foobar'. The parser assigned the string 'DISTANCE=3000' to that attribute in the args namespace, which turned into a dictionary key:value pair in arguments.

You could, of course, look for arguments['**kwargs'], and parse the value for yourself:

v = arguments['**kwargs']  # or pop if you prefer
if v is not None:
    k, v = v.split('=')
    arguments[k] = int(v)

It could be generalized to handle multiple pairs (defined with `nargs='*').


argparse does not handle arguments the same way as Python functions, so there's nothing exactly analogous the **kwargs.

The normal way to accept something like distance is with 'optionals' or flagged arguments.

parser.add_argument('-d','--distance', type=int, help=...)

which will accept

python argstest.py location_by_coordinate 40.5949799 -73.9495148 --distance=3000
python argstest.py location_by_coordinate 40.5949799 -73.9495148 --distance 3000
python argstest.py location_by_coordinate 40.5949799 -73.9495148 --d3000
python argstest.py location_by_coordinate 40.5949799 -73.9495148

It could also be setup to use --DISTANCE or other names. In the last case args namespace will have a default value for distance. The default default is None.

That's the straight forward way of adding kwarg like arguments to argparse.

Accepting arbitrary dictionary like pairs, distance:3000, distance=3000, has been asked before on SO. The answers have always been some variation of the parsing that I sketched above. It could be done in a custom Action class, or post parsing as I suggest.

oops, this answer is nearly a clone of one I wrote a few days ago: https://stackoverflow.com/a/33639147/901925

A similar 2011 question: Using argparse to parse arguments of form "arg= val"

Python argparse dict arg

=================================

(edit)

Example with a function that takes *args:

In [2]: import argparse
In [3]: def foo(*args, **kwargs):
   ...:     print('args',args)
   ...:     print('kwargs',kwargs)
   ...:     
In [4]: parser=argparse.ArgumentParser()
In [5]: parser.add_argument('arg1')
In [6]: parser.add_argument('arg2',nargs='+')

In [7]: args=parser.parse_args('one two three'.split())
In [8]: args
Out[8]: Namespace(arg1='one', arg2=['two', 'three'])

So I have 2 positional arguments, one with a single string value, the other with a list (due to the + nargs).

Call foo with these args attributes:

In [10]: foo(args.arg1)
args ('one',)
kwargs {}

In [11]: foo(args.arg1, args.arg2)
args ('one', ['two', 'three'])
kwargs {}

In [12]: foo(args.arg1, arg2=args.arg2)
args ('one',)
kwargs {'arg2': ['two', 'three']}

I defined 'positionals', but it would have worked just as well with 'optionals'. The distinction between positionals and optionals disappears in the namespace.

If I convert the namespace to a dictionary, I can pass values to foo in various ways, either through the *args or through **kwargs. It's all in how I call foo, not in how they appear in args or arguments. None of this is unique to argparse.

In [13]: arguments = vars(args)
In [14]: arguments
Out[14]: {'arg2': ['two', 'three'], 'arg1': 'one'}

In [15]: foo(arguments['arg2'], arguments['arg1'])
args (['two', 'three'], 'one')
kwargs {}

In [16]: foo(arguments['arg2'], arguments)
args (['two', 'three'], {'arg2': ['two', 'three'], 'arg1': 'one'})
kwargs {}

In [17]: foo(arguments['arg2'], **arguments)
args (['two', 'three'],)
kwargs {'arg2': ['two', 'three'], 'arg1': 'one'}

In [24]: foo(*arguments, **arguments)
args ('arg2', 'arg1')             # *args is the keys of arguments
kwargs {'arg2': ['two', 'three'], 'arg1': 'one'}

In [25]: foo(*arguments.values(), **arguments)
args (['two', 'three'], 'one')    # *args is the values of arguments
kwargs {'arg2': ['two', 'three'], 'arg1': 'one'}

这篇关于将 argparse 与采用 **kwargs 参数的函数一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆