簡體   English   中英

使用帶有** kwargs參數的函數的argparse

[英]Using argparse with function that takes **kwargs argument

我正在使用argparse來獲取輸入並將其傳遞給一個函數,該函數將兩個變量和**kwargs作為參數。

這是我的功能:

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

鑒於此功能及其使用** kwargs,我應該如何設置subparsers?

這是我到目前為止設置命令行解析器的方法:

 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()

顯然,當我在命令行中調用它時,上面的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: error: unrecognized arguments: DISTANCE=3000

但我不確定如何為** kwargs設置subparser。 如果我嘗試設置這樣的subparser:

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' }

並返回此Traceback:

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

如何啟用argparse來處理/解析在命令行輸入的內容,該命令行將通過** kwargs傳遞給函數?

你明白發生了什么嗎?

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

arguments字典? 您使用'** kwargs'的名稱('dest')定義了一個'位置'參數。 你可以把它命名為'foobar'。 解析器分配字符串“DISTANCE = 3000”在該屬性args名字空間,它變成了一個字典項:在值對arguments

當然,您可以查找arguments['**kwargs'] ,並為自己解析值:

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

它可以推廣到處理多對(用`nargs ='*'定義)。


argparse不像Python函數那樣處理參數,所以**kwargs並沒有完全類似的東西。

接受像distance這樣的東西的正常方法是使用'optionals'或標記的參數。

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

哪個會接受

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

它也可以設置為使用--DISTANCE或其他名稱。 在最后一種情況下, args命名空間將具有distance的默認值。 默認默認值為None

這是向argparse添加kwarg like參數的直接方式。

接受任意字典,如對, distance:3000distance=3000 ,之前已被問過SO。 答案一直是我在上面概述的解析的一些變化。 它可以在自定義Action類中完成,或者按照我的建議進行解析。

哎呀,這個答案幾乎是我幾天前寫的一個克隆: https//stackoverflow.com/a/33639147/901925

一個類似的2011年問題: 使用argparse來解析形式為“arg = val”的參數

Python argparse dict arg

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

(編輯)

*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'])

所以我有2個位置參數,一個帶有單個字符串值,另一個帶有列表(由於+ nargs)。

使用這些args屬性調用foo

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']}

我定義了'位置',但它與'選項'一樣有效。 位置和選項之間的區別在命名空間中消失。

如果我將命名空間轉換為字典,我可以通過*args**kwargs以各種方式將值傳遞給foo 這就是我如何調用foo ,而不是它們如何出現在argsarguments 這些都不是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傳遞給函數?

這個命令:

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

不執行函數調用:

location_by_coordinate(40.5949799, -73.9495148, DISTANCE=3000)

這很容易證明:

def location_by_coordinate(x, y, **kwargs):
    print "I was called!"

繼續解析args,你會看到函數沒有被調用。 因此,您設置名為location_by_coordinate的subparser的所有工作都是徒勞的。

argparse模塊只檢查sys.argv ,這是一個簡單的字符串列表。 每個字符串都是python命令后在命令行中輸入的“單詞”之一。

默認情況下,參數字符串取自sys.argv,...
https://docs.python.org/3/library/argparse.html#the-parse-args-method

是的, sys.argv是一個可怕的名字,但字符串列表只是一個字符串列表。 如果你看一下argparse文檔,所有的例子都這樣做:

parser.parse_args('--foo FOO'.split())

使用split()創建的字符串列表與sys.argv引用的某些字符串列表沒有什么不同。

您需要自己調用location_by_coordinate()函數。 為了做到這一點,你需要從命令行獲取args,在字典中組裝應該是kwargs的args,然后像這樣調用你的函數:

location_by_coordinate(lat, lon, **my_dict)

如果您有這些值:

lat = 10
lon = 20
my_dict = {'a': 1, 'b': 2}

那么上面的函數調用將相當於:

location_by_coordinate(10, 20, a=1, b=2)

這是一個例子:

import argparse

def dostuff(x, y, **kwargs):
    print x, y, kwargs

parser = argparse.ArgumentParser()
parser.add_argument("LAT")
parser.add_argument("LON")
parser.add_argument("--distance")
args = parser.parse_args()
my_dict = {}
my_dict["distance"] = args.distance

dostuff(args.LAT, args.LON, **my_dict)

$ python my_prog.py 10 20 --distance 1
10 20 {'distance': '1'}

你也可以從解析器中獲取一個dict:

...
...
args = parser.parse_args()
args_dict = vars(args)
print args_dict

--output:--
{'LAT': '10', 'distance': '1', 'LON': '20'}

lat = args_dict.pop('LAT')
lon = args_dict.pop('LON')
print args_dict

--output:--
{'distance': '1'}

location_by_coordinates(lat, lon, **args_dict)

如果要使用戶鍵入:

DISTANCE=3000

在命令行上,首先我不會讓他們鍵入所有大寫字母,所以讓我們制定目標:

distance=3000

向解析器添加另一個必需參數:

location_by_parser.add_argument("distance", help="distance")

然后在解析以下內容之后:

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

你可以這樣做:

arguments = parser.parse_args()
args_dict = vars(arguments)

args_dict將包含鍵/值對'distance': 'distance=3000' 您可以通過執行以下操作將該dict條目更改為'distance': '3000'

pieces = args_dict['distance'].split('=')

if len(pieces) == 2 and pieces[0] == 'distance':
    args_dict['distance'] = pieces[1]

或者,您可以進行設置,以便解析器通過創建在解析distance arg時執行的自定義操作來自動執行該代碼:

class DistanceAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        #values => The value for the distance command line arg
        pieces = values.split('=')

        if len(pieces) == 2 and pieces[0] in ['distance', 'wave_action']:  #only allow 'distance=' and 'wave_action='
            setattr(namespace, self.dest, pieces[1]) #The dest key specified in the parser gets assigned the value
        else:
            raise argparse.ArgumentTypeError('Usage: distance=3000.  Only distance=, wave_action= allowed.')

你可以使用這樣的動作:

location_by_parser.add_argument(
    "distance", 
    help="longitude", 
    action=DistanceAction
)

如果你想得到想象,你可以將命令行中指定的所有name=val args收集到一個名為keyword_args字典中,這樣你就可以調用你的方法:

args = parser.parse_args()
args_dict = vars(args)
keyword_args = args_dict["keyword_args"]

location_by_coordinates(lat, lon, **keyword_args)

這是解析器配置:

location_by_parser.add_argument(
    "keyword_args", 
    help="extra args", 
    nargs='*', 
    action=DistanceAction
)

import argparse
import sys

def location_by_coordinates(x, y, **kwargs):
    print x 
    print y
    print kwargs

class DistanceAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        allowed_keywords = ['distance', 'wave_action']
        keyword_dict = {}

        for arg in values:  #values => The args found for keyword_args
            pieces = arg.split('=')

            if len(pieces) == 2 and pieces[0] in allowed_keywords:
                keyword_dict[pieces[0]] = pieces[1]
            else: #raise an error                                                         
                #Create error message:
                msg_inserts = ['{}='] * len(allowed_keywords)
                msg_template = 'Example usage: distance=3000. Only {} allowed.'.format(', '.join(msg_inserts))
                msg = msg_template.format(*allowed_keywords)

                raise argparse.ArgumentTypeError(msg)

        setattr(namespace, self.dest, keyword_dict) #The dest key specified in the
                                                    #parser gets assigned the keyword_dict--in
                                                    #this case it defaults to 'keyword_args'

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")
location_by_parser.add_argument("keyword_args", help="extra args", nargs='*', action=DistanceAction)

arguments = parser.parse_args()
args_dict = vars(arguments)

print args_dict

lat = args_dict['LAT']
lon = args_dict['LNG']
keyword_args = args_dict['keyword_args']

location_by_coordinates(lat, lon, **keyword_args)

例:

$ python prog.py location_by_coordinate 40.5949799 -73.9495148 distance=3000 wave_action=1.4

{'LAT': '40.5949799', 'LNG': '-73.9495148', 'command': 'location_by_coordinate', 'keyword_args': {'distance': '3000', 'wave_action': '1.4'}}

40.5949799
-73.9495148
{'distance': '3000', 'wave_action': '1.4'}

$ python prog.py location_by_coordinate 40.5949799 -73.9495148 x=10
...
...
  File "2.py", line 25, in __call__
    raise argparse.ArgumentTypeError(msg)
argparse.ArgumentTypeError: Example usage: distance=3000. Only distance=, wave_action= allowed.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM