我有一个带有函数的程序,它接受类初始化器和对象列表。每个对象由3个变量id,value和tag组成。
class Package():
def __init__(self, id, value, name):
if (value <= 0):
raise ValueError("Amount must be greater than 0")
self.id = id
self.value = value
self.tag = tag
class Purchase():
def submit(some_list):
//Do stuff
def main():
//Help here!
parser = argparse.ArgumentParser()
parser.add_argument("id", help="ID")
parser.add_argument("value", help="Value")
parser.add_argument("tag", help="Tag")
args = parser.parse_args()
some_list = [args.id, args.value, args.tag]
submit(some_list)
我正在尝试在main()中实现argparse,所以我可以通过执行以下操作来运行程序:python foo.py "int0 [(int1, float1, int2), (int3, float2, int4) ....]"
。列表中的对象数是可变的,取决于用户输入。
initializer = num0
//First package object
package.id = num1
package.value = num2
package.tag = num3
//Second package object
package.id = num4
package.value = num5
package.tag = num6
答案 0 :(得分:2)
您可以制作自定义argument type 并使用ast.literal_eval()
来解析该值。
工作样本:
import argparse
from ast import literal_eval
class Package():
def __init__(self, id, value, tag):
if (value <= 0):
raise ValueError("Amount must be greater than 0")
self.id = id
self.value = value
self.tag = tag
def packages(s):
try:
data = literal_eval(s)
except: # TODO: avoid bare except and handle more specific errors
raise argparse.ArgumentTypeError("Invalid 'packages' format.")
return [Package(*item) for item in data]
parser = argparse.ArgumentParser()
parser.add_argument('--packages', dest="packages", type=packages, nargs=1)
args = parser.parse_args()
print(args.packages)
现在,如果您要运行该脚本,您将获得打印的Package
类实例列表:
$ python test.py --packages="[(1, 1.02, 3), (40, 2.32, 11)]"
[[<__main__.Package instance at 0x10a20d368>, <__main__.Package instance at 0x10a20d4d0>]]
答案 1 :(得分:2)
我希望更加明确并使用自定义操作:
import argparse
class PackageAction(argparse.Action):
def __init__(self, *args, **kwargs):
super(PackageAction, self).__init__(*args, **kwargs)
self.nargs = 3
def __call__(self, parser, namespace, values, option_string):
lst = getattr(namespace, self.dest, []) or []
a, b, c = values
lst.append(Package(int(a), float(b), int(c)))
setattr(namespace, self.dest, lst)
class Package(object):
def __init__(self, foo, bar, baz):
self.foo = foo
self.bar = bar
self.baz = baz
def __repr__(self):
return 'Package(%r, %r, %r)' % (self.foo, self.bar, self.baz)
parser = argparse.ArgumentParser()
parser.add_argument('--package', action=PackageAction)
print(parser.parse_args())
这里的用法类似于:
$ python packager.py --package 1 2 3 --package 4 5 6
Namespace(package=[Package(1, 2.0, 3), Package(4, 5.0, 6)])
一个好处是你可以获得更好的默认错误处理......例如:
$ python ~/sandbox/test.py --package 1 2 3 --package 4 5
usage: test.py [-h] [--package PACKAGE PACKAGE PACKAGE]
test.py: error: argument --package: expected 3 argument(s)
当然,您可以根据自己的目的进行修改 - 特别是为__call__
提供额外的error handling可能会更好。例如你可以做点什么
parser.error('--package requires an int float and int')
如果用户传递了错误的字符串。您还可以提供更好的变量名称: - )
答案 2 :(得分:1)
这是我的提名;它使用普通的解析器,并将自定义放在Package
类中。
它会被称为:
python prog.py -p 0 1 2 --package 2 3 4
其中-p
或--package
后跟3个值,可能会重复(action
是'追加')。 nargs=3
确保每个-p
后跟3个值(否则解析器会引发错误)。将这些值转换为数字(并引发错误)是Package
类的责任。该课程已经检查了非负面value
。
import argparse
class Package():
def __init__(self, id, value, tag):
# 3 inputs - numbers, but equivalent strings are accepted
# may add more value validation
self.id = int(id)
self.value = float(value)
if self.value <= 0:
raise ValueError("Amount must be greater than 0")
self.tag = int(tag)
def __repr__(self):
return 'Package (%s, %s, %s)'%(self.id, self.value, self.tag)
def main(argv):
parser = argparse.ArgumentParser()
parser.add_argument('-p', '--package', nargs=3, action='append', default=[],
metavar=('ID','Value','tag'), help='package parameters; may repeat')
args = parser.parse_args(argv)
print args
packages = [Package(*v) for v in args.package]
return packages
# alt
# args.package = packages; return args
if __name__ == '__main__':
import sys
if sys.argv[1:]:
print main(sys.argv[1:])
else:
# test cases
print main([]) # nothing
print main('-p 1 2 3'.split())
print main('-p 0 1 2 --pack 2 3 4'.split())
print main(['-h']) # help
测试用例的示例运行是:
2030:~/mypy$ python stack34823075.py
Namespace(package=[])
[]
Namespace(package=[['1', '2', '3']])
[Package (1, 2.0, 3)]
Namespace(package=[['0', '1', '2'], ['2', '3', '4']])
[Package (0, 1.0, 2), Package (2, 3.0, 4)]
usage: stack34823075.py [-h] [-p ID Value tag]
optional arguments:
-h, --help show this help message and exit
-p ID Value tag, --package ID Value tag
package parameters; may repeat
请注意metavar
如何影响帮助显示。 Package
__repr__
方法会生成一个很好的列表显示。
使用非数字tag
运行的示例:
2038:~/mypy$ python stack34823075.py -p 1 2.3 tag
Namespace(package=[['1', '2.3', 'tag']])
Traceback (most recent call last):
File "stack34823075.py", line 31, in <module>
print main(sys.argv[1:])
File "stack34823075.py", line 20, in main
packages = [Package(*v) for v in args.package if v is not None]
File "stack34823075.py", line 10, in __init__
self.tag = int(tag)
ValueError: invalid literal for int() with base 10: 'tag'
特殊的type
功能在这里效果不佳。它将分别应用于3个字符串中的每个字符串,而不是作为一个组。
自定义Action
类可以处理3个值,将每个值转换为int
,float
,int
。但即使在那里,我也希望将它们传递给Package
,例如
def __call__(self, namespace, dest, values):
# store_action style
new_value = Package(*values)
setattr(namespace, dest, new_value) # store action
但由于packages = [Package(*v) for v in args.package]
非常简单,我在定制解析器或其操作方面没有太多意义。