Python: 从命令行调用时如何启用 kwargs? (也许与 argparse)
Python: How can I enable use of kwargs when calling from command line? (perhaps with argparse)
假设我有模块myscript.py;此模块是生产代码,通常称为 %dir%>python myscript.py foo bar
。
我想扩展它以接受关键字参数。我知道我可以使用下面的脚本获取这些参数,但不幸的是,必须使用
来调用它
%dir%>python myscript.py main(foo, bar)
.
我知道我可以使用 argparse
模块,但我不确定如何使用。
import sys
def main(foo,bar,**kwargs):
print 'Called myscript with:'
print 'foo = %s' % foo
print 'bar = %s' % bar
if kwargs:
for k in kwargs.keys():
print 'keyword argument : %s' % k + ' = ' + '%s' % kwargs[k]
if __name__=="__main__":
exec(''.join(sys.argv[1:]))
首先,您不会将任意 Python 表达式作为参数传递。它很脆弱且不安全。
要设置参数解析器,您可以定义所需的参数,然后解析它们以生成一个 Namespace
对象,其中包含命令行调用指定的信息。
import argparse
p = argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('bar')
p.add_argument('--add-feature-a', dest='a', action='store_true', default=False)
在您的 __main__
块中,您将解析参数,然后将从 Namespace
生成的字典传递给 main
。
if __name__ == '__main__':
args = p.parse_args()
main(**vars(args))
然后您将使用类似
的行调用您的脚本
# foo = "3", bar = "6", a = True
python myscript.py 3 6 --add-feature-a
或
# foo = "moo", bar="7.7", a = False
python myscript.py moo 7.7
您可以使用 argparse
做更多的事情,但这是一个简单的示例,用于将它产生的值转化为 main
。
如果你想像在主函数中一样传递关键字参数,key=value
,你可以这样做:
import sys
def main(foo, bar, *args):
print "Called my script with"
print "foo = %s" % foo
print "bar = %s" % bar
for arg in args:
k = arg.split("=")[0]
v = arg.split("=")[1]
print "Keyword argument: %s = %s" % (k, v)
if __name__ == "__main__":
if len(sys.argv) < 3:
raise SyntaxError("Insufficient arguments.")
if len(sys.argv) != 3:
# If there are keyword arguments
main(sys.argv[1], sys.argv[2], *sys.argv[3:])
else:
# If there are no keyword arguments
main(sys.argv[1], sys.argv[2])
一些例子:
$> python my_file.py a b x=4
Called my script with
foo = a
bar = b
Keyword argument: x = 4
$> python my_file.py foo bar key=value
Called my script with
foo = foo
bar = bar
Keyword argument: key = value
但是,这假设键和值之间没有任何空格,key = value
将不起作用。
如果您正在寻找 --argument
种关键字参数,您 应该 使用 argparse
.
@Moon 用类似的解决方案打败了我,但我建议事先进行解析并传入实际 kwargs
:
import sys
def main(foo, bar, **kwargs):
print('Called myscript with:')
print('foo = {}'.format(foo))
print('bar = {}'.format(bar))
for k, v in kwargs.items():
print('keyword argument: {} = {}'.format(k, v))
if __name__=='__main__':
main(sys.argv[1], # foo
sys.argv[2], # bar
**dict(arg.split('=') for arg in sys.argv[3:])) # kwargs
# Example use:
# $ python myscript.py foo bar hello=world 'with spaces'='a value'
# Called myscript with:
# foo = foo
# bar = bar
# keyword argument: hello = world
# keyword argument: with spaces = a value
通过一些内省,可以从函数的签名中设置 ArgumentParser
,从而将命令行参数直接映射到函数参数:
import argparse
import inspect
def myfun(mode, count=1, frobify=False, *files):
print('Doing %s %d times on %s (%sfrobifying)' % (
mode, count, files, '' if frobify else 'not '
))
def funopt(fun, argv=None):
parser = argparse.ArgumentParser()
if hasattr(inspect, 'getfullargspec'):
spec = inspect.getfullargspec(fun)
else:
spec = inspect.getargspec(fun)
num_defaults = len(spec.defaults) if spec.defaults is not None else 0
for i in range(len(spec.args)):
if i < len(spec.args) - num_defaults:
parser.add_argument(spec.args[i])
elif spec.defaults[i - len(spec.args)] is False:
parser.add_argument('--' + spec.args[i],
default=False, action='store_true')
else:
default = spec.defaults[i - len(spec.args)]
parser.add_argument('--' + spec.args[i],
default=default,
type=type(default))
if spec.varargs is not None:
parser.add_argument(spec.varargs,
nargs='*')
kwargs = vars(parser.parse_args(argv))
args = []
for arg in spec.args:
args += [kwargs[arg]]
if spec.varargs is not None:
args += kwargs[spec.varargs]
fun(*args)
funopt(myfun)
结果:
$ python test.py
usage: test.py [-h] [--count COUNT] [--frobify] mode [files [files ...]]
test.py: error: too few arguments
$ python test.py myaction a b c
Doing myaction 1 times on ('a', 'b', 'c') (not frobifying)
$ python test.py --frobify --count=5 myaction a b c
Doing myaction 5 times on ('a', 'b', 'c') (frobifying)
在两行代码中,我可以获得可以像标准 args 和 kwargs 一样操作的 args 和 kwargs:
import sys
if __name__=='__main__':
argv=argv[1:]
kwargs={kw[0]:kw[1] for kw in [ar.split('=') for ar in argv if ar.find('=')>0]}
args=[arg for arg in argv if arg.find('=')<0]
#and you can the use args and kwargs as so:
if 'reset' in args:
do_some_functions_with_reset()
a_device_var=kwargs.get('device',False):
#or whatever you want to do with args and kwargs
结果是:
$python test.py reset device=foo format=1 bar
->args=['reset','bar']
->kwargs={'device':'foo','format':'1'}
假设我有模块myscript.py;此模块是生产代码,通常称为 %dir%>python myscript.py foo bar
。
我想扩展它以接受关键字参数。我知道我可以使用下面的脚本获取这些参数,但不幸的是,必须使用
来调用它%dir%>python myscript.py main(foo, bar)
.
我知道我可以使用 argparse
模块,但我不确定如何使用。
import sys
def main(foo,bar,**kwargs):
print 'Called myscript with:'
print 'foo = %s' % foo
print 'bar = %s' % bar
if kwargs:
for k in kwargs.keys():
print 'keyword argument : %s' % k + ' = ' + '%s' % kwargs[k]
if __name__=="__main__":
exec(''.join(sys.argv[1:]))
首先,您不会将任意 Python 表达式作为参数传递。它很脆弱且不安全。
要设置参数解析器,您可以定义所需的参数,然后解析它们以生成一个 Namespace
对象,其中包含命令行调用指定的信息。
import argparse
p = argparse.ArgumentParser()
p.add_argument('foo')
p.add_argument('bar')
p.add_argument('--add-feature-a', dest='a', action='store_true', default=False)
在您的 __main__
块中,您将解析参数,然后将从 Namespace
生成的字典传递给 main
。
if __name__ == '__main__':
args = p.parse_args()
main(**vars(args))
然后您将使用类似
的行调用您的脚本# foo = "3", bar = "6", a = True
python myscript.py 3 6 --add-feature-a
或
# foo = "moo", bar="7.7", a = False
python myscript.py moo 7.7
您可以使用 argparse
做更多的事情,但这是一个简单的示例,用于将它产生的值转化为 main
。
如果你想像在主函数中一样传递关键字参数,key=value
,你可以这样做:
import sys
def main(foo, bar, *args):
print "Called my script with"
print "foo = %s" % foo
print "bar = %s" % bar
for arg in args:
k = arg.split("=")[0]
v = arg.split("=")[1]
print "Keyword argument: %s = %s" % (k, v)
if __name__ == "__main__":
if len(sys.argv) < 3:
raise SyntaxError("Insufficient arguments.")
if len(sys.argv) != 3:
# If there are keyword arguments
main(sys.argv[1], sys.argv[2], *sys.argv[3:])
else:
# If there are no keyword arguments
main(sys.argv[1], sys.argv[2])
一些例子:
$> python my_file.py a b x=4
Called my script with
foo = a
bar = b
Keyword argument: x = 4
$> python my_file.py foo bar key=value
Called my script with
foo = foo
bar = bar
Keyword argument: key = value
但是,这假设键和值之间没有任何空格,key = value
将不起作用。
如果您正在寻找 --argument
种关键字参数,您 应该 使用 argparse
.
@Moon 用类似的解决方案打败了我,但我建议事先进行解析并传入实际 kwargs
:
import sys
def main(foo, bar, **kwargs):
print('Called myscript with:')
print('foo = {}'.format(foo))
print('bar = {}'.format(bar))
for k, v in kwargs.items():
print('keyword argument: {} = {}'.format(k, v))
if __name__=='__main__':
main(sys.argv[1], # foo
sys.argv[2], # bar
**dict(arg.split('=') for arg in sys.argv[3:])) # kwargs
# Example use:
# $ python myscript.py foo bar hello=world 'with spaces'='a value'
# Called myscript with:
# foo = foo
# bar = bar
# keyword argument: hello = world
# keyword argument: with spaces = a value
通过一些内省,可以从函数的签名中设置 ArgumentParser
,从而将命令行参数直接映射到函数参数:
import argparse
import inspect
def myfun(mode, count=1, frobify=False, *files):
print('Doing %s %d times on %s (%sfrobifying)' % (
mode, count, files, '' if frobify else 'not '
))
def funopt(fun, argv=None):
parser = argparse.ArgumentParser()
if hasattr(inspect, 'getfullargspec'):
spec = inspect.getfullargspec(fun)
else:
spec = inspect.getargspec(fun)
num_defaults = len(spec.defaults) if spec.defaults is not None else 0
for i in range(len(spec.args)):
if i < len(spec.args) - num_defaults:
parser.add_argument(spec.args[i])
elif spec.defaults[i - len(spec.args)] is False:
parser.add_argument('--' + spec.args[i],
default=False, action='store_true')
else:
default = spec.defaults[i - len(spec.args)]
parser.add_argument('--' + spec.args[i],
default=default,
type=type(default))
if spec.varargs is not None:
parser.add_argument(spec.varargs,
nargs='*')
kwargs = vars(parser.parse_args(argv))
args = []
for arg in spec.args:
args += [kwargs[arg]]
if spec.varargs is not None:
args += kwargs[spec.varargs]
fun(*args)
funopt(myfun)
结果:
$ python test.py
usage: test.py [-h] [--count COUNT] [--frobify] mode [files [files ...]]
test.py: error: too few arguments
$ python test.py myaction a b c
Doing myaction 1 times on ('a', 'b', 'c') (not frobifying)
$ python test.py --frobify --count=5 myaction a b c
Doing myaction 5 times on ('a', 'b', 'c') (frobifying)
在两行代码中,我可以获得可以像标准 args 和 kwargs 一样操作的 args 和 kwargs:
import sys
if __name__=='__main__':
argv=argv[1:]
kwargs={kw[0]:kw[1] for kw in [ar.split('=') for ar in argv if ar.find('=')>0]}
args=[arg for arg in argv if arg.find('=')<0]
#and you can the use args and kwargs as so:
if 'reset' in args:
do_some_functions_with_reset()
a_device_var=kwargs.get('device',False):
#or whatever you want to do with args and kwargs
结果是:
$python test.py reset device=foo format=1 bar
->args=['reset','bar']
->kwargs={'device':'foo','format':'1'}